/// <summary>Find or create a schema node if <code>createNodes</code> is true.</summary>
		/// <param name="tree">the root of the xmp tree.</param>
		/// <param name="namespaceURI">a namespace</param>
		/// <param name="suggestedPrefix">If a prefix is suggested, the namespace is allowed to be registered.</param>
		/// <param name="createNodes">
		/// a flag indicating if the node shall be created if not found.
		/// <em>Note:</em> The namespace must be registered prior to this call.
		/// </param>
		/// <returns>
		/// Returns the schema node if found, <code>null</code> otherwise.
		/// Note: If <code>createNodes</code> is <code>true</code>, it is <b>always</b>
		/// returned a valid node.
		/// </returns>
		/// <exception cref="Com.Adobe.Xmp.XMPException">
		/// An exception is only thrown if an error occurred, not if a
		/// node was not found.
		/// </exception>
		internal static XMPNode FindSchemaNode(XMPNode tree, string namespaceURI, string suggestedPrefix, bool createNodes)
		{
			System.Diagnostics.Debug.Assert(tree.GetParent() == null);
			// make sure that its the root
			XMPNode schemaNode = tree.FindChildByName(namespaceURI);
			if (schemaNode == null && createNodes)
			{
				schemaNode = new XMPNode(namespaceURI, new PropertyOptions().SetSchemaNode(true));
				schemaNode.SetImplicit(true);
				// only previously registered schema namespaces are allowed in the XMP tree.
				string prefix = XMPMetaFactory.GetSchemaRegistry().GetNamespacePrefix(namespaceURI);
				if (prefix == null)
				{
					if (suggestedPrefix != null && suggestedPrefix.Length != 0)
					{
						prefix = XMPMetaFactory.GetSchemaRegistry().RegisterNamespace(namespaceURI, suggestedPrefix);
					}
					else
					{
						throw new XMPException("Unregistered schema namespace URI", XMPErrorConstants.Badschema);
					}
				}
				schemaNode.SetValue(prefix);
				tree.AddChild(schemaNode);
			}
			return schemaNode;
		}
		/// <summary>
		/// Tweak old XMP: Move an instance ID from rdf:about to the
		/// <em>xmpMM:InstanceID</em> property.
		/// </summary>
		/// <remarks>
		/// Tweak old XMP: Move an instance ID from rdf:about to the
		/// <em>xmpMM:InstanceID</em> property. An old instance ID usually looks
		/// like &quot;uuid:bac965c4-9d87-11d9-9a30-000d936b79c4&quot;, plus InDesign
		/// 3.0 wrote them like &quot;bac965c4-9d87-11d9-9a30-000d936b79c4&quot;. If
		/// the name looks like a UUID simply move it to <em>xmpMM:InstanceID</em>,
		/// don't worry about any existing <em>xmpMM:InstanceID</em>. Both will
		/// only be present when a newer file with the <em>xmpMM:InstanceID</em>
		/// property is updated by an old app that uses <em>rdf:about</em>.
		/// </remarks>
		/// <param name="tree">the root of the metadata tree</param>
		/// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if tweaking fails.</exception>
		private static void TweakOldXMP(XMPNode tree)
		{
			if (tree.GetName() != null && tree.GetName().Length >= Utils.UuidLength)
			{
				string nameStr = tree.GetName().ToLower();
				if (nameStr.StartsWith("uuid:"))
				{
					nameStr = Sharpen.Runtime.Substring(nameStr, 5);
				}
				if (Utils.CheckUUIDFormat(nameStr))
				{
					// move UUID to xmpMM:InstanceID and remove it from the root node
					XMPPath path = XMPPathParser.ExpandXPath(XMPConstConstants.NsXmpMm, "InstanceID");
					XMPNode idNode = XMPNodeUtils.FindNode(tree, path, true, null);
					if (idNode != null)
					{
						idNode.SetOptions(null);
						// Clobber any existing xmpMM:InstanceID.
						idNode.SetValue("uuid:" + nameStr);
						idNode.RemoveChildren();
						idNode.RemoveQualifiers();
						tree.SetName(null);
					}
					else
					{
						throw new XMPException("Failure creating xmpMM:InstanceID", XMPErrorConstants.Internalfailure);
					}
				}
			}
		}
 /// <summary>Appends a language item to an alt text array.</summary>
 /// <param name="arrayNode">the language array</param>
 /// <param name="itemLang">the language of the item</param>
 /// <param name="itemValue">the content of the item</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if a duplicate property is added</exception>
 internal static void AppendLangItem(XMPNode arrayNode, string itemLang, string itemValue)
 {
     XMPNode newItem = new XMPNode(XMPConstConstants.ArrayItemName, itemValue, null);
     XMPNode langQual = new XMPNode(XMPConstConstants.XmlLang, itemLang, null);
     newItem.AddQualifier(langQual);
     if (!XMPConstConstants.XDefault.Equals(langQual.GetValue()))
     {
         arrayNode.AddChild(newItem);
     }
     else
     {
         arrayNode.AddChild(1, newItem);
     }
 }
Example #4
0
        /// <summary>Deletes the the given node and its children from its parent.</summary>
        /// <remarks>
        /// Deletes the the given node and its children from its parent.
        /// Takes care about adjusting the flags.
        /// </remarks>
        /// <param name="node">the top-most node to delete.</param>
        internal static void DeleteNode(XMPNode node)
        {
            XMPNode parent = node.GetParent();

            if (node.GetOptions().IsQualifier())
            {
                // root is qualifier
                parent.RemoveQualifier(node);
            }
            else
            {
                // root is NO qualifier
                parent.RemoveChild(node);
            }
            // delete empty Schema nodes
            if (!parent.HasChildren() && parent.GetOptions().IsSchemaNode())
            {
                parent.GetParent().RemoveChild(parent);
            }
        }
 /// <summary>
 /// Undo the denormalization performed by the XMP used in Acrobat 5.<br />
 /// If a Dublin Core array had only one item, it was serialized as a simple
 /// property.
 /// </summary>
 /// <remarks>
 /// Undo the denormalization performed by the XMP used in Acrobat 5.<br />
 /// If a Dublin Core array had only one item, it was serialized as a simple
 /// property. <br />
 /// The <code>xml:lang</code> attribute was dropped from an
 /// <code>alt-text</code> item if the language was <code>x-default</code>.
 /// </remarks>
 /// <param name="dcSchema">the DC schema node</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if normalization fails</exception>
 private static void NormalizeDCArrays(XMPNode dcSchema)
 {
     for (int i = 1; i <= dcSchema.GetChildrenLength(); i++)
     {
         XMPNode         currProp  = dcSchema.GetChild(i);
         PropertyOptions arrayForm = (PropertyOptions)dcArrayForms.Get(currProp.GetName());
         if (arrayForm == null)
         {
             continue;
         }
         else
         {
             if (currProp.GetOptions().IsSimple())
             {
                 // create a new array and add the current property as child,
                 // if it was formerly simple
                 XMPNode newArray = new XMPNode(currProp.GetName(), arrayForm);
                 currProp.SetName(XMPConstConstants.ArrayItemName);
                 newArray.AddChild(currProp);
                 dcSchema.ReplaceChild(i, newArray);
                 // fix language alternatives
                 if (arrayForm.IsArrayAltText() && !currProp.GetOptions().GetHasLanguage())
                 {
                     XMPNode newLang = new XMPNode(XMPConstConstants.XmlLang, XMPConstConstants.XDefault, null);
                     currProp.AddQualifier(newLang);
                 }
             }
             else
             {
                 // clear array options and add corrected array form if it has been an array before
                 currProp.GetOptions().SetOption(PropertyOptions.Array | PropertyOptions.ArrayOrdered | PropertyOptions.ArrayAlternate | PropertyOptions.ArrayAltText, false);
                 currProp.GetOptions().MergeWith(arrayForm);
                 if (arrayForm.IsArrayAltText())
                 {
                     // applying for "dc:description", "dc:rights", "dc:title"
                     RepairAltText(currProp);
                 }
             }
         }
     }
 }
Example #6
0
 /// <summary>See if an array is an alt-text array.</summary>
 /// <remarks>
 /// See if an array is an alt-text array. If so, make sure the x-default item
 /// is first.
 /// </remarks>
 /// <param name="arrayNode">the array node to check if its an alt-text array</param>
 internal static void DetectAltText(XMPNode arrayNode)
 {
     if (arrayNode.GetOptions().IsArrayAlternate() && arrayNode.HasChildren())
     {
         bool isAltText = false;
         for (Iterator it = arrayNode.IterateChildren(); it.HasNext();)
         {
             XMPNode child = (XMPNode)it.Next();
             if (child.GetOptions().GetHasLanguage())
             {
                 isAltText = true;
                 break;
             }
         }
         if (isAltText)
         {
             arrayNode.GetOptions().SetArrayAltText(true);
             NormalizeLangArray(arrayNode);
         }
     }
 }
Example #7
0
        /// <summary>Utility to find or create the array used by <code>SeparateArrayItems()</code>.</summary>
        /// <param name="schemaNS">a the namespace fo the array</param>
        /// <param name="arrayName">the name of the array</param>
        /// <param name="arrayOptions">the options for the array if newly created</param>
        /// <param name="xmp">the xmp object</param>
        /// <returns>Returns the array node.</returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards exceptions</exception>
        private static XMPNode SeparateFindCreateArray(string schemaNS, string arrayName, PropertyOptions arrayOptions, XMPMetaImpl xmp)
        {
            arrayOptions = XMPNodeUtils.VerifySetOptions(arrayOptions, null);
            if (!arrayOptions.IsOnlyArrayOptions())
            {
                throw new XMPException("Options can only provide array form", XMPErrorConstants.Badoptions);
            }
            // Find the array node, make sure it is OK. Move the current children
            // aside, to be readded later if kept.
            XMPPath arrayPath = XMPPathParser.ExpandXPath(schemaNS, arrayName);
            XMPNode arrayNode = XMPNodeUtils.FindNode(xmp.GetRoot(), arrayPath, false, null);

            if (arrayNode != null)
            {
                // The array exists, make sure the form is compatible. Zero
                // arrayForm means take what exists.
                PropertyOptions arrayForm = arrayNode.GetOptions();
                if (!arrayForm.IsArray() || arrayForm.IsArrayAlternate())
                {
                    throw new XMPException("Named property must be non-alternate array", XMPErrorConstants.Badxpath);
                }
                if (arrayOptions.EqualArrayTypes(arrayForm))
                {
                    throw new XMPException("Mismatch of specified and existing array form", XMPErrorConstants.Badxpath);
                }
            }
            else
            {
                // *** Right error?
                // The array does not exist, try to create it.
                // don't modify the options handed into the method
                arrayNode = XMPNodeUtils.FindNode(xmp.GetRoot(), arrayPath, true, arrayOptions.SetArray(true));
                if (arrayNode == null)
                {
                    throw new XMPException("Failed to create named array", XMPErrorConstants.Badxpath);
                }
            }
            return(arrayNode);
        }
Example #8
0
            /// <param name="currNode">the node that will be added to the path.</param>
            /// <param name="parentPath">the path up to this node.</param>
            /// <param name="currentIndex">the current array index if an arrey is traversed</param>
            /// <returns>Returns the updated path.</returns>
            protected internal virtual string AccumulatePath(XMPNode currNode, string parentPath, int currentIndex)
            {
                string separator;
                string segmentName;

                if (currNode.GetParent() == null || currNode.GetOptions().IsSchemaNode())
                {
                    return(null);
                }
                else
                {
                    if (currNode.GetParent().GetOptions().IsArray())
                    {
                        separator   = string.Empty;
                        segmentName = "[" + currentIndex.ToString() + "]";
                    }
                    else
                    {
                        separator   = "/";
                        segmentName = currNode.GetName();
                    }
                }
                if (parentPath == null || parentPath.Length == 0)
                {
                    return(segmentName);
                }
                else
                {
                    if (this._enclosing.GetOptions().IsJustLeafname())
                    {
                        return(!segmentName.StartsWith("?") ? segmentName : Sharpen.Runtime.Substring(segmentName, 1));
                    }
                    else
                    {
                        // qualifier
                        return(parentPath + separator + segmentName);
                    }
                }
            }
        /// <summary>Fixes the GPS Timestamp in EXIF.</summary>
        /// <param name="exifSchema">the EXIF schema node</param>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if the date conversion fails.</exception>
        private static void FixGPSTimeStamp(XMPNode exifSchema)
        {
            // Note: if dates are not found the convert-methods throws an exceptions,
            //       and this methods returns.
            XMPNode gpsDateTime = XMPNodeUtils.FindChildNode(exifSchema, "exif:GPSTimeStamp", false);

            if (gpsDateTime == null)
            {
                return;
            }
            try
            {
                XMPDateTime binGPSStamp;
                XMPDateTime binOtherDate;
                binGPSStamp = XMPUtils.ConvertToDate(gpsDateTime.GetValue());
                if (binGPSStamp.GetYear() != 0 || binGPSStamp.GetMonth() != 0 || binGPSStamp.GetDay() != 0)
                {
                    return;
                }
                XMPNode otherDate = XMPNodeUtils.FindChildNode(exifSchema, "exif:DateTimeOriginal", false);
                if (otherDate == null)
                {
                    otherDate = XMPNodeUtils.FindChildNode(exifSchema, "exif:DateTimeDigitized", false);
                }
                binOtherDate = XMPUtils.ConvertToDate(otherDate.GetValue());
                Sharpen.Calendar cal = binGPSStamp.GetCalendar();
                cal.Set(Sharpen.CalendarEnum.Year, binOtherDate.GetYear());
                cal.Set(Sharpen.CalendarEnum.Month, binOtherDate.GetMonth());
                cal.Set(Sharpen.CalendarEnum.DayOfMonth, binOtherDate.GetDay());
                binGPSStamp = new XMPDateTimeImpl(cal);
                gpsDateTime.SetValue(XMPUtils.ConvertFromDate(binGPSStamp));
            }
            catch (XMPException)
            {
                // Don't let a missing or bad date stop other things.
                return;
            }
        }
Example #10
0
 /// <summary>Looks for the appropriate language item in a text alternative array.item</summary>
 /// <param name="arrayNode">an array node</param>
 /// <param name="language">the requested language</param>
 /// <returns>Returns the index if the language has been found, -1 otherwise.</returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException"/>
 internal static int LookupLanguageItem(XMPNode arrayNode, string language)
 {
     if (!arrayNode.GetOptions().IsArray())
     {
         throw new XMPException("Language item must be used on array", XMPErrorConstants.Badxpath);
     }
     for (int index = 1; index <= arrayNode.GetChildrenLength(); index++)
     {
         XMPNode child = arrayNode.GetChild(index);
         if (!child.HasQualifier() || !XMPConstConstants.XmlLang.Equals(child.GetQualifier(1).GetName()))
         {
             continue;
         }
         else
         {
             if (language.Equals(child.GetQualifier(1).GetValue()))
             {
                 return(index);
             }
         }
     }
     return(-1);
 }
 /// <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="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception>
 private static void CompareAliasedSubtrees(XMPNode aliasNode, XMPNode baseNode, bool outerCall)
 {
     if (!aliasNode.GetValue().Equals(baseNode.GetValue()) || aliasNode.GetChildrenLength() != baseNode.GetChildrenLength())
     {
         throw new XMPException("Mismatch between alias and base nodes", XMPErrorConstants.Badxmp);
     }
     if (!outerCall && (!aliasNode.GetName().Equals(baseNode.GetName()) || !aliasNode.GetOptions().Equals(baseNode.GetOptions()) || aliasNode.GetQualifierLength() != baseNode.GetQualifierLength()))
     {
         throw new XMPException("Mismatch between alias and base nodes", XMPErrorConstants.Badxmp);
     }
     for (Iterator an = aliasNode.IterateChildren(), bn = baseNode.IterateChildren(); an.HasNext() && bn.HasNext(); )
     {
         XMPNode aliasChild = (XMPNode)an.Next();
         XMPNode baseChild = (XMPNode)bn.Next();
         CompareAliasedSubtrees(aliasChild, baseChild, false);
     }
     for (Iterator an_1 = aliasNode.IterateQualifier(), bn_1 = baseNode.IterateQualifier(); an_1.HasNext() && bn_1.HasNext(); )
     {
         XMPNode aliasQual = (XMPNode)an_1.Next();
         XMPNode baseQual = (XMPNode)bn_1.Next();
         CompareAliasedSubtrees(aliasQual, baseQual, false);
     }
 }
 /// <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="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception>
 private static void CompareAliasedSubtrees(XMPNode aliasNode, XMPNode baseNode, bool outerCall)
 {
     if (!aliasNode.GetValue().Equals(baseNode.GetValue()) || aliasNode.GetChildrenLength() != baseNode.GetChildrenLength())
     {
         throw new XMPException("Mismatch between alias and base nodes", XMPErrorConstants.Badxmp);
     }
     if (!outerCall && (!aliasNode.GetName().Equals(baseNode.GetName()) || !aliasNode.GetOptions().Equals(baseNode.GetOptions()) || aliasNode.GetQualifierLength() != baseNode.GetQualifierLength()))
     {
         throw new XMPException("Mismatch between alias and base nodes", XMPErrorConstants.Badxmp);
     }
     for (Iterator an = aliasNode.IterateChildren(), bn = baseNode.IterateChildren(); an.HasNext() && bn.HasNext();)
     {
         XMPNode aliasChild = (XMPNode)an.Next();
         XMPNode baseChild  = (XMPNode)bn.Next();
         CompareAliasedSubtrees(aliasChild, baseChild, false);
     }
     for (Iterator an_1 = aliasNode.IterateQualifier(), bn_1 = baseNode.IterateQualifier(); an_1.HasNext() && bn_1.HasNext();)
     {
         XMPNode aliasQual = (XMPNode)an_1.Next();
         XMPNode baseQual  = (XMPNode)bn_1.Next();
         CompareAliasedSubtrees(aliasQual, baseQual, false);
     }
 }
 /// <summary>Make sure that the array is well-formed AltText.</summary>
 /// <remarks>
 /// Make sure that the array is well-formed AltText. Each item must be simple
 /// and have an "xml:lang" qualifier. If repairs are needed, keep simple
 /// non-empty items by adding the "xml:lang" with value "x-repair".
 /// </remarks>
 /// <param name="arrayNode">the property node of the array to repair.</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards unexpected exceptions.</exception>
 private static void RepairAltText(XMPNode arrayNode)
 {
     if (arrayNode == null || !arrayNode.GetOptions().IsArray())
     {
         // Already OK or not even an array.
         return;
     }
     // fix options
     arrayNode.GetOptions().SetArrayOrdered(true).SetArrayAlternate(true).SetArrayAltText(true);
     for (Iterator it = arrayNode.IterateChildren(); it.HasNext();)
     {
         XMPNode currChild = (XMPNode)it.Next();
         if (currChild.GetOptions().IsCompositeProperty())
         {
             // Delete non-simple children.
             it.Remove();
         }
         else
         {
             if (!currChild.GetOptions().GetHasLanguage())
             {
                 string childValue = currChild.GetValue();
                 if (childValue == null || childValue.Length == 0)
                 {
                     // Delete empty valued children that have no xml:lang.
                     it.Remove();
                 }
                 else
                 {
                     // Add an xml:lang qualifier with the value "x-repair".
                     XMPNode repairLang = new XMPNode(XMPConstConstants.XmlLang, "x-repair", null);
                     currChild.AddQualifier(repairLang);
                 }
             }
         }
     }
 }
Example #14
0
 /// <summary>
 /// Searches for a qualifier selector in a node:
 /// [?qualName="value"] - an element in an array, chosen by a qualifier value.
 /// </summary>
 /// <remarks>
 /// Searches for a qualifier selector in a node:
 /// [?qualName="value"] - an element in an array, chosen by a qualifier value.
 /// No implicit nodes are created for qualifier selectors,
 /// except for an alias to an x-default item.
 /// </remarks>
 /// <param name="arrayNode">an array node</param>
 /// <param name="qualName">the qualifier name</param>
 /// <param name="qualValue">the qualifier value</param>
 /// <param name="aliasForm">
 /// in case the qual selector results from an alias,
 /// an x-default node is created if there has not been one.
 /// </param>
 /// <returns>Returns the index of th</returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException"></exception>
 private static int LookupQualSelector(XMPNode arrayNode, string qualName, string qualValue, int aliasForm)
 {
     if (XMPConstConstants.XmlLang.Equals(qualName))
     {
         qualValue = Utils.NormalizeLangValue(qualValue);
         int index = Com.Adobe.Xmp.Impl.XMPNodeUtils.LookupLanguageItem(arrayNode, qualValue);
         if (index < 0 && (aliasForm & AliasOptions.PropArrayAltText) > 0)
         {
             XMPNode langNode = new XMPNode(XMPConstConstants.ArrayItemName, null);
             XMPNode xdefault = new XMPNode(XMPConstConstants.XmlLang, XMPConstConstants.XDefault, null);
             langNode.AddQualifier(xdefault);
             arrayNode.AddChild(1, langNode);
             return(1);
         }
         else
         {
             return(index);
         }
     }
     else
     {
         for (int index = 1; index < arrayNode.GetChildrenLength(); index++)
         {
             XMPNode currItem = arrayNode.GetChild(index);
             for (Iterator it = currItem.IterateQualifier(); it.HasNext();)
             {
                 XMPNode qualifier = (XMPNode)it.Next();
                 if (qualName.Equals(qualifier.GetName()) && qualValue.Equals(qualifier.GetValue()))
                 {
                     return(index);
                 }
             }
         }
         return(-1);
     }
 }
		/// <summary>Constructor for a cloned metadata tree.</summary>
		/// <param name="tree">
		/// an prefilled metadata tree which fulfills all
		/// <code>XMPNode</code> contracts.
		/// </param>
		public XMPMetaImpl(XMPNode tree)
		{
			this.tree = tree;
		}
		/// <summary>Constructor for an empty metadata object.</summary>
		public XMPMetaImpl()
		{
			// create root node
			tree = new XMPNode(null, null, null);
		}
		/// <summary>
		/// Evaluates a raw node value to the given value type, apply special
		/// conversions for defined types in XMP.
		/// </summary>
		/// <param name="valueType">an int indicating the value type</param>
		/// <param name="propNode">the node containing the value</param>
		/// <returns>Returns a literal value for the node.</returns>
		/// <exception cref="Com.Adobe.Xmp.XMPException"/>
		private object EvaluateNodeValue(int valueType, XMPNode propNode)
		{
			object value;
			string rawValue = propNode.GetValue();
			switch (valueType)
			{
				case ValueBoolean:
				{
					value = XMPUtils.ConvertToBoolean(rawValue);
					break;
				}

				case ValueInteger:
				{
					value = XMPUtils.ConvertToInteger(rawValue);
					break;
				}

				case ValueLong:
				{
					value = XMPUtils.ConvertToLong(rawValue);
					break;
				}

				case ValueDouble:
				{
					value = XMPUtils.ConvertToDouble(rawValue);
					break;
				}

				case ValueDate:
				{
					value = XMPUtils.ConvertToDate(rawValue);
					break;
				}

				case ValueCalendar:
				{
					XMPDateTime dt = XMPUtils.ConvertToDate(rawValue);
					value = dt.GetCalendar();
					break;
				}

				case ValueBase64:
				{
					value = XMPUtils.DecodeBase64(rawValue);
					break;
				}

				case ValueString:
				default:
				{
					// leaf values return empty string instead of null
					// for the other cases the converter methods provides a "null"
					// value.
					// a default value can only occur if this method is made public.
					value = rawValue != null || propNode.GetOptions().IsCompositeProperty() ? rawValue : string.Empty;
					break;
				}
			}
			return value;
		}
		// -------------------------------------------------------------------------------------
		// private
		/// <summary>Locate or create the item node and set the value.</summary>
		/// <remarks>
		/// Locate or create the item node and set the value. Note the index
		/// parameter is one-based! The index can be in the range [1..size + 1] or
		/// "last()", normalize it and check the insert flags. The order of the
		/// normalization checks is important. If the array is empty we end up with
		/// an index and location to set item size + 1.
		/// </remarks>
		/// <param name="arrayNode">an array node</param>
		/// <param name="itemIndex">the index where to insert the item</param>
		/// <param name="itemValue">the item value</param>
		/// <param name="itemOptions">the options for the new item</param>
		/// <param name="insert">insert oder overwrite at index position?</param>
		/// <exception cref="Com.Adobe.Xmp.XMPException"/>
		private void DoSetArrayItem(XMPNode arrayNode, int itemIndex, string itemValue, PropertyOptions itemOptions, bool insert)
		{
			XMPNode itemNode = new XMPNode(XMPConstConstants.ArrayItemName, null);
			itemOptions = XMPNodeUtils.VerifySetOptions(itemOptions, itemValue);
			// in insert mode the index after the last is allowed,
			// even ArrayLastItem points to the index *after* the last.
			int maxIndex = insert ? arrayNode.GetChildrenLength() + 1 : arrayNode.GetChildrenLength();
			if (itemIndex == XMPConstConstants.ArrayLastItem)
			{
				itemIndex = maxIndex;
			}
			if (1 <= itemIndex && itemIndex <= maxIndex)
			{
				if (!insert)
				{
					arrayNode.RemoveChild(itemIndex);
				}
				arrayNode.AddChild(itemIndex, itemNode);
				SetNode(itemNode, itemValue, itemOptions, false);
			}
			else
			{
				throw new XMPException("Array index out of bounds", XMPErrorConstants.Badindex);
			}
		}
 /// <summary>Remove all empty schemas from the metadata tree that were generated during the rdf parsing.</summary>
 /// <param name="tree">the root of the metadata tree</param>
 private static void DeleteEmptySchemas(XMPNode tree)
 {
     // Delete empty schema nodes. Do this last, other cleanup can make empty
     // schema.
     for (Iterator it = tree.IterateChildren(); it.HasNext(); )
     {
         XMPNode schema = (XMPNode)it.Next();
         if (!schema.HasChildren())
         {
             it.Remove();
         }
     }
 }
 /// <summary>This is setting the value of a leaf node.</summary>
 /// <param name="node">an XMPNode</param>
 /// <param name="value">a value</param>
 internal static void SetNodeValue(XMPNode node, object value)
 {
     string strValue = SerializeNodeValue(value);
     if (!(node.GetOptions().IsQualifier() && XMPConstConstants.XmlLang.Equals(node.GetName())))
     {
         node.SetValue(strValue);
     }
     else
     {
         node.SetValue(Utils.NormalizeLangValue(strValue));
     }
 }
Example #21
0
        /// <summary>
        /// <ol>
        /// <li>Look for an exact match with the specific language.
        /// </summary>
        /// <remarks>
        /// <ol>
        /// <li>Look for an exact match with the specific language.
        /// <li>If a generic language is given, look for partial matches.
        /// <li>Look for an "x-default"-item.
        /// <li>Choose the first item.
        /// </ol>
        /// </remarks>
        /// <param name="arrayNode">the alt text array node</param>
        /// <param name="genericLang">the generic language</param>
        /// <param name="specificLang">the specific language</param>
        /// <returns>
        /// Returns the kind of match as an Integer and the found node in an
        /// array.
        /// </returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException"/>
        internal static object[] ChooseLocalizedText(XMPNode arrayNode, string genericLang, string specificLang)
        {
            // See if the array has the right form. Allow empty alt arrays,
            // that is what parsing returns.
            if (!arrayNode.GetOptions().IsArrayAltText())
            {
                throw new XMPException("Localized text array is not alt-text", XMPErrorConstants.Badxpath);
            }
            else
            {
                if (!arrayNode.HasChildren())
                {
                    return(new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltNoValues, null });
                }
            }
            int     foundGenericMatches = 0;
            XMPNode resultNode          = null;
            XMPNode xDefault            = null;

            // Look for the first partial match with the generic language.
            for (Iterator it = arrayNode.IterateChildren(); it.HasNext();)
            {
                XMPNode currItem = (XMPNode)it.Next();
                // perform some checks on the current item
                if (currItem.GetOptions().IsCompositeProperty())
                {
                    throw new XMPException("Alt-text array item is not simple", XMPErrorConstants.Badxpath);
                }
                else
                {
                    if (!currItem.HasQualifier() || !XMPConstConstants.XmlLang.Equals(currItem.GetQualifier(1).GetName()))
                    {
                        throw new XMPException("Alt-text array item has no language qualifier", XMPErrorConstants.Badxpath);
                    }
                }
                string currLang = currItem.GetQualifier(1).GetValue();
                // Look for an exact match with the specific language.
                if (specificLang.Equals(currLang))
                {
                    return(new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltSpecificMatch, currItem });
                }
                else
                {
                    if (genericLang != null && currLang.StartsWith(genericLang))
                    {
                        if (resultNode == null)
                        {
                            resultNode = currItem;
                        }
                        // ! Don't return/break, need to look for other matches.
                        foundGenericMatches++;
                    }
                    else
                    {
                        if (XMPConstConstants.XDefault.Equals(currLang))
                        {
                            xDefault = currItem;
                        }
                    }
                }
            }
            // evaluate loop
            if (foundGenericMatches == 1)
            {
                return(new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltSingleGeneric, resultNode });
            }
            else
            {
                if (foundGenericMatches > 1)
                {
                    return(new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltMultipleGeneric, resultNode });
                }
                else
                {
                    if (xDefault != null)
                    {
                        return(new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltXdefault, xDefault });
                    }
                    else
                    {
                        // Everything failed, choose the first item.
                        return(new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltFirstItem, arrayNode.GetChild(1) });
                    }
                }
            }
        }
 /// <summary>Moves an alias node of array form to another schema into an array</summary>
 /// <param name="propertyIt">the property iterator of the old schema (used to delete the property)</param>
 /// <param name="childNode">the node to be moved</param>
 /// <param name="baseArray">the base array for the array item</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception>
 private static void TransplantArrayItemAlias(Iterator propertyIt, XMPNode childNode, XMPNode baseArray)
 {
     if (baseArray.GetOptions().IsArrayAltText())
     {
         if (childNode.GetOptions().GetHasLanguage())
         {
             throw new XMPException("Alias to x-default already has a language qualifier", XMPErrorConstants.Badxmp);
         }
         XMPNode langQual = new XMPNode(XMPConstConstants.XmlLang, XMPConstConstants.XDefault, null);
         childNode.AddQualifier(langQual);
     }
     propertyIt.Remove();
     childNode.SetName(XMPConstConstants.ArrayItemName);
     baseArray.AddChild(childNode);
 }
 /// <summary>Make sure that the array is well-formed AltText.</summary>
 /// <remarks>
 /// Make sure that the array is well-formed AltText. Each item must be simple
 /// and have an "xml:lang" qualifier. If repairs are needed, keep simple
 /// non-empty items by adding the "xml:lang" with value "x-repair".
 /// </remarks>
 /// <param name="arrayNode">the property node of the array to repair.</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards unexpected exceptions.</exception>
 private static void RepairAltText(XMPNode arrayNode)
 {
     if (arrayNode == null || !arrayNode.GetOptions().IsArray())
     {
         // Already OK or not even an array.
         return;
     }
     // fix options
     arrayNode.GetOptions().SetArrayOrdered(true).SetArrayAlternate(true).SetArrayAltText(true);
     for (Iterator it = arrayNode.IterateChildren(); it.HasNext(); )
     {
         XMPNode currChild = (XMPNode)it.Next();
         if (currChild.GetOptions().IsCompositeProperty())
         {
             // Delete non-simple children.
             it.Remove();
         }
         else
         {
             if (!currChild.GetOptions().GetHasLanguage())
             {
                 string childValue = currChild.GetValue();
                 if (childValue == null || childValue.Length == 0)
                 {
                     // Delete empty valued children that have no xml:lang.
                     it.Remove();
                 }
                 else
                 {
                     // Add an xml:lang qualifier with the value "x-repair".
                     XMPNode repairLang = new XMPNode(XMPConstConstants.XmlLang, "x-repair", null);
                     currChild.AddQualifier(repairLang);
                 }
             }
         }
     }
 }
 /// <summary>
 /// Undo the denormalization performed by the XMP used in Acrobat 5.<br />
 /// If a Dublin Core array had only one item, it was serialized as a simple
 /// property.
 /// </summary>
 /// <remarks>
 /// Undo the denormalization performed by the XMP used in Acrobat 5.<br />
 /// If a Dublin Core array had only one item, it was serialized as a simple
 /// property. <br />
 /// The <code>xml:lang</code> attribute was dropped from an
 /// <code>alt-text</code> item if the language was <code>x-default</code>.
 /// </remarks>
 /// <param name="dcSchema">the DC schema node</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if normalization fails</exception>
 private static void NormalizeDCArrays(XMPNode dcSchema)
 {
     for (int i = 1; i <= dcSchema.GetChildrenLength(); i++)
     {
         XMPNode currProp = dcSchema.GetChild(i);
         PropertyOptions arrayForm = (PropertyOptions)dcArrayForms.Get(currProp.GetName());
         if (arrayForm == null)
         {
             continue;
         }
         else
         {
             if (currProp.GetOptions().IsSimple())
             {
                 // create a new array and add the current property as child,
                 // if it was formerly simple
                 XMPNode newArray = new XMPNode(currProp.GetName(), arrayForm);
                 currProp.SetName(XMPConstConstants.ArrayItemName);
                 newArray.AddChild(currProp);
                 dcSchema.ReplaceChild(i, newArray);
                 // fix language alternatives
                 if (arrayForm.IsArrayAltText() && !currProp.GetOptions().GetHasLanguage())
                 {
                     XMPNode newLang = new XMPNode(XMPConstConstants.XmlLang, XMPConstConstants.XDefault, null);
                     currProp.AddQualifier(newLang);
                 }
             }
             else
             {
                 // clear array options and add corrected array form if it has been an array before
                 currProp.GetOptions().SetOption(PropertyOptions.Array | PropertyOptions.ArrayOrdered | PropertyOptions.ArrayAlternate | PropertyOptions.ArrayAltText, false);
                 currProp.GetOptions().MergeWith(arrayForm);
                 if (arrayForm.IsArrayAltText())
                 {
                     // applying for "dc:description", "dc:rights", "dc:title"
                     RepairAltText(currProp);
                 }
             }
         }
     }
 }
 /// <summary>Visit all of the top level nodes looking for aliases.</summary>
 /// <remarks>
 /// Visit all of the top level nodes looking for aliases. If there is
 /// no base, transplant the alias subtree. If there is a base and strict
 /// aliasing is on, make sure the alias and base subtrees match.
 /// </remarks>
 /// <param name="tree">the root of the metadata tree</param>
 /// <param name="options">th parsing options</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception>
 private static void MoveExplicitAliases(XMPNode tree, ParseOptions options)
 {
     if (!tree.GetHasAliases())
     {
         return;
     }
     tree.SetHasAliases(false);
     bool strictAliasing = options.GetStrictAliasing();
     for (Iterator schemaIt = tree.GetUnmodifiableChildren().Iterator(); schemaIt.HasNext(); )
     {
         XMPNode currSchema = (XMPNode)schemaIt.Next();
         if (!currSchema.GetHasAliases())
         {
             continue;
         }
         for (Iterator propertyIt = currSchema.IterateChildren(); propertyIt.HasNext(); )
         {
             XMPNode currProp = (XMPNode)propertyIt.Next();
             if (!currProp.IsAlias())
             {
                 continue;
             }
             currProp.SetAlias(false);
             // Find the base path, look for the base schema and root node.
             XMPAliasInfo info = XMPMetaFactory.GetSchemaRegistry().FindAlias(currProp.GetName());
             if (info != null)
             {
                 // find or create schema
                 XMPNode baseSchema = XMPNodeUtils.FindSchemaNode(tree, info.GetNamespace(), null, true);
                 baseSchema.SetImplicit(false);
                 XMPNode baseNode = XMPNodeUtils.FindChildNode(baseSchema, info.GetPrefix() + info.GetPropName(), false);
                 if (baseNode == null)
                 {
                     if (info.GetAliasForm().IsSimple())
                     {
                         // A top-to-top alias, transplant the property.
                         // change the alias property name to the base name
                         string qname = info.GetPrefix() + info.GetPropName();
                         currProp.SetName(qname);
                         baseSchema.AddChild(currProp);
                         // remove the alias property
                         propertyIt.Remove();
                     }
                     else
                     {
                         // An alias to an array item,
                         // create the array and transplant the property.
                         baseNode = new XMPNode(info.GetPrefix() + info.GetPropName(), info.GetAliasForm().ToPropertyOptions());
                         baseSchema.AddChild(baseNode);
                         TransplantArrayItemAlias(propertyIt, currProp, baseNode);
                     }
                 }
                 else
                 {
                     if (info.GetAliasForm().IsSimple())
                     {
                         // The base node does exist and this is a top-to-top alias.
                         // Check for conflicts if strict aliasing is on.
                         // Remove and delete the alias subtree.
                         if (strictAliasing)
                         {
                             CompareAliasedSubtrees(currProp, baseNode, true);
                         }
                         propertyIt.Remove();
                     }
                     else
                     {
                         // This is an alias to an array item and the array exists.
                         // Look for the aliased item.
                         // Then transplant or check & delete as appropriate.
                         XMPNode itemNode = null;
                         if (info.GetAliasForm().IsArrayAltText())
                         {
                             int xdIndex = XMPNodeUtils.LookupLanguageItem(baseNode, XMPConstConstants.XDefault);
                             if (xdIndex != -1)
                             {
                                 itemNode = baseNode.GetChild(xdIndex);
                             }
                         }
                         else
                         {
                             if (baseNode.HasChildren())
                             {
                                 itemNode = baseNode.GetChild(1);
                             }
                         }
                         if (itemNode == null)
                         {
                             TransplantArrayItemAlias(propertyIt, currProp, baseNode);
                         }
                         else
                         {
                             if (strictAliasing)
                             {
                                 CompareAliasedSubtrees(currProp, itemNode, true);
                             }
                             propertyIt.Remove();
                         }
                     }
                 }
             }
         }
         currSchema.SetHasAliases(false);
     }
 }
 /// <summary>
 /// The initial support for WAV files mapped a legacy ID3 audio copyright
 /// into a new xmpDM:copyright property.
 /// </summary>
 /// <remarks>
 /// The initial support for WAV files mapped a legacy ID3 audio copyright
 /// into a new xmpDM:copyright property. This is special case code to migrate
 /// that into dc:rights['x-default']. The rules:
 /// <pre>
 /// 1. If there is no dc:rights array, or an empty array -
 /// Create one with dc:rights['x-default'] set from double linefeed and xmpDM:copyright.
 /// 2. If there is a dc:rights array but it has no x-default item -
 /// Create an x-default item as a copy of the first item then apply rule #3.
 /// 3. If there is a dc:rights array with an x-default item,
 /// Look for a double linefeed in the value.
 /// A. If no double linefeed, compare the x-default value to the xmpDM:copyright value.
 /// A1. If they match then leave the x-default value alone.
 /// A2. Otherwise, append a double linefeed and
 /// the xmpDM:copyright value to the x-default value.
 /// B. If there is a double linefeed, compare the trailing text to the xmpDM:copyright value.
 /// B1. If they match then leave the x-default value alone.
 /// B2. Otherwise, replace the trailing x-default text with the xmpDM:copyright value.
 /// 4. In all cases, delete the xmpDM:copyright property.
 /// </pre>
 /// </remarks>
 /// <param name="xmp">the metadata object</param>
 /// <param name="dmCopyright">the "dm:copyright"-property</param>
 private static void MigrateAudioCopyright(XMPMeta xmp, XMPNode dmCopyright)
 {
     try
     {
         XMPNode dcSchema = XMPNodeUtils.FindSchemaNode(((XMPMetaImpl)xmp).GetRoot(), XMPConstConstants.NsDc, true);
         string dmValue = dmCopyright.GetValue();
         string doubleLF = "\n\n";
         XMPNode dcRightsArray = XMPNodeUtils.FindChildNode(dcSchema, "dc:rights", false);
         if (dcRightsArray == null || !dcRightsArray.HasChildren())
         {
             // 1. No dc:rights array, create from double linefeed and xmpDM:copyright.
             dmValue = doubleLF + dmValue;
             xmp.SetLocalizedText(XMPConstConstants.NsDc, "rights", string.Empty, XMPConstConstants.XDefault, dmValue, null);
         }
         else
         {
             int xdIndex = XMPNodeUtils.LookupLanguageItem(dcRightsArray, XMPConstConstants.XDefault);
             if (xdIndex < 0)
             {
                 // 2. No x-default item, create from the first item.
                 string firstValue = dcRightsArray.GetChild(1).GetValue();
                 xmp.SetLocalizedText(XMPConstConstants.NsDc, "rights", string.Empty, XMPConstConstants.XDefault, firstValue, null);
                 xdIndex = XMPNodeUtils.LookupLanguageItem(dcRightsArray, XMPConstConstants.XDefault);
             }
             // 3. Look for a double linefeed in the x-default value.
             XMPNode defaultNode = dcRightsArray.GetChild(xdIndex);
             string defaultValue = defaultNode.GetValue();
             int lfPos = defaultValue.IndexOf(doubleLF);
             if (lfPos < 0)
             {
                 // 3A. No double LF, compare whole values.
                 if (!dmValue.Equals(defaultValue))
                 {
                     // 3A2. Append the xmpDM:copyright to the x-default
                     // item.
                     defaultNode.SetValue(defaultValue + doubleLF + dmValue);
                 }
             }
             else
             {
                 // 3B. Has double LF, compare the tail.
                 if (!Sharpen.Runtime.Substring(defaultValue, lfPos + 2).Equals(dmValue))
                 {
                     // 3B2. Replace the x-default tail.
                     defaultNode.SetValue(Sharpen.Runtime.Substring(defaultValue, 0, lfPos + 2) + dmValue);
                 }
             }
         }
         // 4. Get rid of the xmpDM:copyright.
         dmCopyright.GetParent().RemoveChild(dmCopyright);
     }
     catch (XMPException)
     {
     }
 }
 /// <summary>Fixes the GPS Timestamp in EXIF.</summary>
 /// <param name="exifSchema">the EXIF schema node</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if the date conversion fails.</exception>
 private static void FixGPSTimeStamp(XMPNode exifSchema)
 {
     // Note: if dates are not found the convert-methods throws an exceptions,
     // 		 and this methods returns.
     XMPNode gpsDateTime = XMPNodeUtils.FindChildNode(exifSchema, "exif:GPSTimeStamp", false);
     if (gpsDateTime == null)
     {
         return;
     }
     try
     {
         XMPDateTime binGPSStamp;
         XMPDateTime binOtherDate;
         binGPSStamp = XMPUtils.ConvertToDate(gpsDateTime.GetValue());
         if (binGPSStamp.GetYear() != 0 || binGPSStamp.GetMonth() != 0 || binGPSStamp.GetDay() != 0)
         {
             return;
         }
         XMPNode otherDate = XMPNodeUtils.FindChildNode(exifSchema, "exif:DateTimeOriginal", false);
         if (otherDate == null)
         {
             otherDate = XMPNodeUtils.FindChildNode(exifSchema, "exif:DateTimeDigitized", false);
         }
         binOtherDate = XMPUtils.ConvertToDate(otherDate.GetValue());
         Sharpen.Calendar cal = binGPSStamp.GetCalendar();
         cal.Set(Sharpen.CalendarEnum.Year, binOtherDate.GetYear());
         cal.Set(Sharpen.CalendarEnum.Month, binOtherDate.GetMonth());
         cal.Set(Sharpen.CalendarEnum.DayOfMonth, binOtherDate.GetDay());
         binGPSStamp = new XMPDateTimeImpl(cal);
         gpsDateTime.SetValue(XMPUtils.ConvertFromDate(binGPSStamp));
     }
     catch (XMPException)
     {
         // Don't let a missing or bad date stop other things.
         return;
     }
 }
Example #28
0
        /// <summary>
        /// After processing by ExpandXPath, a step can be of these forms:
        /// <ul>
        /// <li>qualName - A top level property or struct field.
        /// </summary>
        /// <remarks>
        /// After processing by ExpandXPath, a step can be of these forms:
        /// <ul>
        /// <li>qualName - A top level property or struct field.
        /// <li>[index] - An element of an array.
        /// <li>[last()] - The last element of an array.
        /// <li>[qualName="value"] - An element in an array of structs, chosen by a field value.
        /// <li>[?qualName="value"] - An element in an array, chosen by a qualifier value.
        /// <li>?qualName - A general qualifier.
        /// </ul>
        /// Find the appropriate child node, resolving aliases, and optionally creating nodes.
        /// </remarks>
        /// <param name="parentNode">the node to start to start from</param>
        /// <param name="nextStep">the xpath segment</param>
        /// <param name="createNodes"></param>
        /// <returns>returns the found or created XMPPath node</returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException"></exception>
        private static XMPNode FollowXPathStep(XMPNode parentNode, XMPPathSegment nextStep, bool createNodes)
        {
            XMPNode nextNode = null;
            int     index    = 0;
            int     stepKind = nextStep.GetKind();

            if (stepKind == XMPPath.StructFieldStep)
            {
                nextNode = FindChildNode(parentNode, nextStep.GetName(), createNodes);
            }
            else
            {
                if (stepKind == XMPPath.QualifierStep)
                {
                    nextNode = FindQualifierNode(parentNode, Sharpen.Runtime.Substring(nextStep.GetName(), 1), createNodes);
                }
                else
                {
                    // This is an array indexing step. First get the index, then get the node.
                    if (!parentNode.GetOptions().IsArray())
                    {
                        throw new XMPException("Indexing applied to non-array", XMPErrorConstants.Badxpath);
                    }
                    if (stepKind == XMPPath.ArrayIndexStep)
                    {
                        index = FindIndexedItem(parentNode, nextStep.GetName(), createNodes);
                    }
                    else
                    {
                        if (stepKind == XMPPath.ArrayLastStep)
                        {
                            index = parentNode.GetChildrenLength();
                        }
                        else
                        {
                            if (stepKind == XMPPath.FieldSelectorStep)
                            {
                                string[] result     = Utils.SplitNameAndValue(nextStep.GetName());
                                string   fieldName  = result[0];
                                string   fieldValue = result[1];
                                index = LookupFieldSelector(parentNode, fieldName, fieldValue);
                            }
                            else
                            {
                                if (stepKind == XMPPath.QualSelectorStep)
                                {
                                    string[] result    = Utils.SplitNameAndValue(nextStep.GetName());
                                    string   qualName  = result[0];
                                    string   qualValue = result[1];
                                    index = LookupQualSelector(parentNode, qualName, qualValue, nextStep.GetAliasForm());
                                }
                                else
                                {
                                    throw new XMPException("Unknown array indexing step in FollowXPathStep", XMPErrorConstants.Internalfailure);
                                }
                            }
                        }
                    }
                    if (1 <= index && index <= parentNode.GetChildrenLength())
                    {
                        nextNode = parentNode.GetChild(index);
                    }
                }
            }
            return(nextNode);
        }
 /// <summary>
 /// Searches for a qualifier selector in a node:
 /// [?qualName="value"] - an element in an array, chosen by a qualifier value.
 /// </summary>
 /// <remarks>
 /// Searches for a qualifier selector in a node:
 /// [?qualName="value"] - an element in an array, chosen by a qualifier value.
 /// No implicit nodes are created for qualifier selectors,
 /// except for an alias to an x-default item.
 /// </remarks>
 /// <param name="arrayNode">an array node</param>
 /// <param name="qualName">the qualifier name</param>
 /// <param name="qualValue">the qualifier value</param>
 /// <param name="aliasForm">
 /// in case the qual selector results from an alias,
 /// an x-default node is created if there has not been one.
 /// </param>
 /// <returns>Returns the index of th</returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException"></exception>
 private static int LookupQualSelector(XMPNode arrayNode, string qualName, string qualValue, int aliasForm)
 {
     if (XMPConstConstants.XmlLang.Equals(qualName))
     {
         qualValue = Utils.NormalizeLangValue(qualValue);
         int index = Com.Adobe.Xmp.Impl.XMPNodeUtils.LookupLanguageItem(arrayNode, qualValue);
         if (index < 0 && (aliasForm & AliasOptions.PropArrayAltText) > 0)
         {
             XMPNode langNode = new XMPNode(XMPConstConstants.ArrayItemName, null);
             XMPNode xdefault = new XMPNode(XMPConstConstants.XmlLang, XMPConstConstants.XDefault, null);
             langNode.AddQualifier(xdefault);
             arrayNode.AddChild(1, langNode);
             return 1;
         }
         else
         {
             return index;
         }
     }
     else
     {
         for (int index = 1; index < arrayNode.GetChildrenLength(); index++)
         {
             XMPNode currItem = arrayNode.GetChild(index);
             for (Iterator it = currItem.IterateQualifier(); it.HasNext(); )
             {
                 XMPNode qualifier = (XMPNode)it.Next();
                 if (qualName.Equals(qualifier.GetName()) && qualValue.Equals(qualifier.GetValue()))
                 {
                     return index;
                 }
             }
         }
         return -1;
     }
 }
        /// <summary>Visit all of the top level nodes looking for aliases.</summary>
        /// <remarks>
        /// Visit all of the top level nodes looking for aliases. If there is
        /// no base, transplant the alias subtree. If there is a base and strict
        /// aliasing is on, make sure the alias and base subtrees match.
        /// </remarks>
        /// <param name="tree">the root of the metadata tree</param>
        /// <param name="options">th parsing options</param>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception>
        private static void MoveExplicitAliases(XMPNode tree, ParseOptions options)
        {
            if (!tree.GetHasAliases())
            {
                return;
            }
            tree.SetHasAliases(false);
            bool strictAliasing = options.GetStrictAliasing();

            for (Iterator schemaIt = tree.GetUnmodifiableChildren().Iterator(); schemaIt.HasNext();)
            {
                XMPNode currSchema = (XMPNode)schemaIt.Next();
                if (!currSchema.GetHasAliases())
                {
                    continue;
                }
                for (Iterator propertyIt = currSchema.IterateChildren(); propertyIt.HasNext();)
                {
                    XMPNode currProp = (XMPNode)propertyIt.Next();
                    if (!currProp.IsAlias())
                    {
                        continue;
                    }
                    currProp.SetAlias(false);
                    // Find the base path, look for the base schema and root node.
                    XMPAliasInfo info = XMPMetaFactory.GetSchemaRegistry().FindAlias(currProp.GetName());
                    if (info != null)
                    {
                        // find or create schema
                        XMPNode baseSchema = XMPNodeUtils.FindSchemaNode(tree, info.GetNamespace(), null, true);
                        baseSchema.SetImplicit(false);
                        XMPNode baseNode = XMPNodeUtils.FindChildNode(baseSchema, info.GetPrefix() + info.GetPropName(), false);
                        if (baseNode == null)
                        {
                            if (info.GetAliasForm().IsSimple())
                            {
                                // A top-to-top alias, transplant the property.
                                // change the alias property name to the base name
                                string qname = info.GetPrefix() + info.GetPropName();
                                currProp.SetName(qname);
                                baseSchema.AddChild(currProp);
                                // remove the alias property
                                propertyIt.Remove();
                            }
                            else
                            {
                                // An alias to an array item,
                                // create the array and transplant the property.
                                baseNode = new XMPNode(info.GetPrefix() + info.GetPropName(), info.GetAliasForm().ToPropertyOptions());
                                baseSchema.AddChild(baseNode);
                                TransplantArrayItemAlias(propertyIt, currProp, baseNode);
                            }
                        }
                        else
                        {
                            if (info.GetAliasForm().IsSimple())
                            {
                                // The base node does exist and this is a top-to-top alias.
                                // Check for conflicts if strict aliasing is on.
                                // Remove and delete the alias subtree.
                                if (strictAliasing)
                                {
                                    CompareAliasedSubtrees(currProp, baseNode, true);
                                }
                                propertyIt.Remove();
                            }
                            else
                            {
                                // This is an alias to an array item and the array exists.
                                // Look for the aliased item.
                                // Then transplant or check & delete as appropriate.
                                XMPNode itemNode = null;
                                if (info.GetAliasForm().IsArrayAltText())
                                {
                                    int xdIndex = XMPNodeUtils.LookupLanguageItem(baseNode, XMPConstConstants.XDefault);
                                    if (xdIndex != -1)
                                    {
                                        itemNode = baseNode.GetChild(xdIndex);
                                    }
                                }
                                else
                                {
                                    if (baseNode.HasChildren())
                                    {
                                        itemNode = baseNode.GetChild(1);
                                    }
                                }
                                if (itemNode == null)
                                {
                                    TransplantArrayItemAlias(propertyIt, currProp, baseNode);
                                }
                                else
                                {
                                    if (strictAliasing)
                                    {
                                        CompareAliasedSubtrees(currProp, itemNode, true);
                                    }
                                    propertyIt.Remove();
                                }
                            }
                        }
                    }
                }
                currSchema.SetHasAliases(false);
            }
        }
 /// <summary>
 /// Searches for a field selector in a node:
 /// [fieldName="value] - an element in an array of structs, chosen by a field value.
 /// </summary>
 /// <remarks>
 /// Searches for a field selector in a node:
 /// [fieldName="value] - an element in an array of structs, chosen by a field value.
 /// No implicit nodes are created by field selectors.
 /// </remarks>
 /// <param name="arrayNode"/>
 /// <param name="fieldName"/>
 /// <param name="fieldValue"/>
 /// <returns>Returns the index of the field if found, otherwise -1.</returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException"></exception>
 private static int LookupFieldSelector(XMPNode arrayNode, string fieldName, string fieldValue)
 {
     int result = -1;
     for (int index = 1; index <= arrayNode.GetChildrenLength() && result < 0; index++)
     {
         XMPNode currItem = arrayNode.GetChild(index);
         if (!currItem.GetOptions().IsStruct())
         {
             throw new XMPException("Field selector must be used on array of struct", XMPErrorConstants.Badxpath);
         }
         for (int f = 1; f <= currItem.GetChildrenLength(); f++)
         {
             XMPNode currField = currItem.GetChild(f);
             if (!fieldName.Equals(currField.GetName()))
             {
                 continue;
             }
             if (fieldValue.Equals(currField.GetValue()))
             {
                 result = index;
                 break;
             }
         }
     }
     return result;
 }
Example #32
0
        /// <summary>Constructor with optionsl initial values.</summary>
        /// <remarks>
        /// Constructor with optionsl initial values. If <code>propName</code> is provided,
        /// <code>schemaNS</code> has also be provided.
        /// </remarks>
        /// <param name="xmp">the iterated metadata object.</param>
        /// <param name="schemaNS">the iteration is reduced to this schema (optional)</param>
        /// <param name="propPath">the iteration is redurce to this property within the <code>schemaNS</code></param>
        /// <param name="options">
        /// advanced iteration options, see
        /// <see cref="Com.Adobe.Xmp.Options.IteratorOptions"/>
        /// </param>
        /// <exception cref="Com.Adobe.Xmp.XMPException">If the node defined by the paramters is not existing.</exception>
        public XMPIteratorImpl(XMPMetaImpl xmp, string schemaNS, string propPath, IteratorOptions options)
        {
            // make sure that options is defined at least with defaults
            this.options = options != null ? options : new IteratorOptions();
            // the start node of the iteration depending on the schema and property filter
            XMPNode startNode    = null;
            string  initialPath  = null;
            bool    baseSchema   = schemaNS != null && schemaNS.Length > 0;
            bool    baseProperty = propPath != null && propPath.Length > 0;

            if (!baseSchema && !baseProperty)
            {
                // complete tree will be iterated
                startNode = xmp.GetRoot();
            }
            else
            {
                if (baseSchema && baseProperty)
                {
                    // Schema and property node provided
                    XMPPath path = XMPPathParser.ExpandXPath(schemaNS, propPath);
                    // base path is the prop path without the property leaf
                    XMPPath basePath = new XMPPath();
                    for (int i = 0; i < path.Size() - 1; i++)
                    {
                        basePath.Add(path.GetSegment(i));
                    }
                    startNode   = XMPNodeUtils.FindNode(xmp.GetRoot(), path, false, null);
                    baseNS      = schemaNS;
                    initialPath = basePath.ToString();
                }
                else
                {
                    if (baseSchema && !baseProperty)
                    {
                        // Only Schema provided
                        startNode = XMPNodeUtils.FindSchemaNode(xmp.GetRoot(), schemaNS, false);
                    }
                    else
                    {
                        // !baseSchema  &&  baseProperty
                        // No schema but property provided -> error
                        throw new XMPException("Schema namespace URI is required", XMPErrorConstants.Badschema);
                    }
                }
            }
            // create iterator
            if (startNode != null)
            {
                if (!this.options.IsJustChildren())
                {
                    nodeIterator = new XMPIteratorImpl.NodeIterator(this, startNode, initialPath, 1);
                }
                else
                {
                    nodeIterator = new XMPIteratorImpl.NodeIteratorChildren(this, startNode, initialPath);
                }
            }
            else
            {
                // create null iterator
                nodeIterator = Collections.EmptyList().ListIterator();
            }
        }
		/// <summary>
		/// see
		/// <see cref="Com.Adobe.Xmp.XMPUtils.SeparateArrayItems(Com.Adobe.Xmp.XMPMeta, string, string, string, Com.Adobe.Xmp.Options.PropertyOptions, bool)"/>
		/// </summary>
		/// <param name="xmp">The XMP object containing the array to be updated.</param>
		/// <param name="schemaNS">
		/// The schema namespace URI for the array. Must not be null or
		/// the empty string.
		/// </param>
		/// <param name="arrayName">
		/// The name of the array. May be a general path expression, must
		/// not be null or the empty string. Each item in the array must
		/// be a simple string value.
		/// </param>
		/// <param name="catedStr">The string to be separated into the array items.</param>
		/// <param name="arrayOptions">Option flags to control the separation.</param>
		/// <param name="preserveCommas">Flag if commas shall be preserved</param>
		/// <exception cref="Com.Adobe.Xmp.XMPException">Forwards the Exceptions from the metadata processing</exception>
		public static void SeparateArrayItems(XMPMeta xmp, string schemaNS, string arrayName, string catedStr, PropertyOptions arrayOptions, bool preserveCommas)
		{
			ParameterAsserts.AssertSchemaNS(schemaNS);
			ParameterAsserts.AssertArrayName(arrayName);
			if (catedStr == null)
			{
				throw new XMPException("Parameter must not be null", XMPErrorConstants.Badparam);
			}
			ParameterAsserts.AssertImplementation(xmp);
			XMPMetaImpl xmpImpl = (XMPMetaImpl)xmp;
			// Keep a zero value, has special meaning below.
			XMPNode arrayNode = SeparateFindCreateArray(schemaNS, arrayName, arrayOptions, xmpImpl);
			// Extract the item values one at a time, until the whole input string is done.
			string itemValue;
			int itemStart;
			int itemEnd;
			int nextKind = UckNormal;
			int charKind = UckNormal;
			char ch = (char) 0;
			char nextChar = (char) 0;
			itemEnd = 0;
			int endPos = catedStr.Length;
			while (itemEnd < endPos)
			{
				// Skip any leading spaces and separation characters. Always skip commas here.
				// They can be kept when within a value, but not when alone between values.
				for (itemStart = itemEnd; itemStart < endPos; itemStart++)
				{
					ch = catedStr[itemStart];
					charKind = ClassifyCharacter(ch);
					if (charKind == UckNormal || charKind == UckQuote)
					{
						break;
					}
				}
				if (itemStart >= endPos)
				{
					break;
				}
				if (charKind != UckQuote)
				{
					// This is not a quoted value. Scan for the end, create an array
					// item from the substring.
					for (itemEnd = itemStart; itemEnd < endPos; itemEnd++)
					{
						ch = catedStr[itemEnd];
						charKind = ClassifyCharacter(ch);
						if (charKind == UckNormal || charKind == UckQuote || (charKind == UckComma && preserveCommas))
						{
							continue;
						}
						else
						{
							if (charKind != UckSpace)
							{
								break;
							}
							else
							{
								if ((itemEnd + 1) < endPos)
								{
									ch = catedStr[itemEnd + 1];
									nextKind = ClassifyCharacter(ch);
									if (nextKind == UckNormal || nextKind == UckQuote || (nextKind == UckComma && preserveCommas))
									{
										continue;
									}
								}
							}
						}
						// Anything left?
						break;
					}
					// Have multiple spaces, or a space followed by a
					// separator.
					itemValue = Sharpen.Runtime.Substring(catedStr, itemStart, itemEnd);
				}
				else
				{
					// Accumulate quoted values into a local string, undoubling
					// internal quotes that
					// match the surrounding quotes. Do not undouble "unmatching"
					// quotes.
					char openQuote = ch;
					char closeQuote = GetClosingQuote(openQuote);
					itemStart++;
					// Skip the opening quote;
					itemValue = string.Empty;
					for (itemEnd = itemStart; itemEnd < endPos; itemEnd++)
					{
						ch = catedStr[itemEnd];
						charKind = ClassifyCharacter(ch);
						if (charKind != UckQuote || !IsSurroundingQuote(ch, openQuote, closeQuote))
						{
							// This is not a matching quote, just append it to the
							// item value.
							itemValue += ch;
						}
						else
						{
							// This is a "matching" quote. Is it doubled, or the
							// final closing quote?
							// Tolerate various edge cases like undoubled opening
							// (non-closing) quotes,
							// or end of input.
							if ((itemEnd + 1) < endPos)
							{
								nextChar = catedStr[itemEnd + 1];
								nextKind = ClassifyCharacter(nextChar);
							}
							else
							{
								nextKind = UckSemicolon;
								nextChar = (char)unchecked((int)(0x3B));
							}
							if (ch == nextChar)
							{
								// This is doubled, copy it and skip the double.
								itemValue += ch;
								// Loop will add in charSize.
								itemEnd++;
							}
							else
							{
								if (!IsClosingingQuote(ch, openQuote, closeQuote))
								{
									// This is an undoubled, non-closing quote, copy it.
									itemValue += ch;
								}
								else
								{
									// This is an undoubled closing quote, skip it and
									// exit the loop.
									itemEnd++;
									break;
								}
							}
						}
					}
				}
				// Add the separated item to the array. 
				// Keep a matching old value in case it had separators.
				int foundIndex = -1;
				for (int oldChild = 1; oldChild <= arrayNode.GetChildrenLength(); oldChild++)
				{
					if (itemValue.Equals(arrayNode.GetChild(oldChild).GetValue()))
					{
						foundIndex = oldChild;
						break;
					}
				}
				XMPNode newItem = null;
				if (foundIndex < 0)
				{
					newItem = new XMPNode(XMPConstConstants.ArrayItemName, itemValue, null);
					arrayNode.AddChild(newItem);
				}
			}
		}
 /// <summary>
 /// <ol>
 /// <li>Look for an exact match with the specific language.
 /// </summary>
 /// <remarks>
 /// <ol>
 /// <li>Look for an exact match with the specific language.
 /// <li>If a generic language is given, look for partial matches.
 /// <li>Look for an "x-default"-item.
 /// <li>Choose the first item.
 /// </ol>
 /// </remarks>
 /// <param name="arrayNode">the alt text array node</param>
 /// <param name="genericLang">the generic language</param>
 /// <param name="specificLang">the specific language</param>
 /// <returns>
 /// Returns the kind of match as an Integer and the found node in an
 /// array.
 /// </returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException"/>
 internal static object[] ChooseLocalizedText(XMPNode arrayNode, string genericLang, string specificLang)
 {
     // See if the array has the right form. Allow empty alt arrays,
     // that is what parsing returns.
     if (!arrayNode.GetOptions().IsArrayAltText())
     {
         throw new XMPException("Localized text array is not alt-text", XMPErrorConstants.Badxpath);
     }
     else
     {
         if (!arrayNode.HasChildren())
         {
             return new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltNoValues, null };
         }
     }
     int foundGenericMatches = 0;
     XMPNode resultNode = null;
     XMPNode xDefault = null;
     // Look for the first partial match with the generic language.
     for (Iterator it = arrayNode.IterateChildren(); it.HasNext(); )
     {
         XMPNode currItem = (XMPNode)it.Next();
         // perform some checks on the current item
         if (currItem.GetOptions().IsCompositeProperty())
         {
             throw new XMPException("Alt-text array item is not simple", XMPErrorConstants.Badxpath);
         }
         else
         {
             if (!currItem.HasQualifier() || !XMPConstConstants.XmlLang.Equals(currItem.GetQualifier(1).GetName()))
             {
                 throw new XMPException("Alt-text array item has no language qualifier", XMPErrorConstants.Badxpath);
             }
         }
         string currLang = currItem.GetQualifier(1).GetValue();
         // Look for an exact match with the specific language.
         if (specificLang.Equals(currLang))
         {
             return new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltSpecificMatch, currItem };
         }
         else
         {
             if (genericLang != null && currLang.StartsWith(genericLang))
             {
                 if (resultNode == null)
                 {
                     resultNode = currItem;
                 }
                 // ! Don't return/break, need to look for other matches.
                 foundGenericMatches++;
             }
             else
             {
                 if (XMPConstConstants.XDefault.Equals(currLang))
                 {
                     xDefault = currItem;
                 }
             }
         }
     }
     // evaluate loop
     if (foundGenericMatches == 1)
     {
         return new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltSingleGeneric, resultNode };
     }
     else
     {
         if (foundGenericMatches > 1)
         {
             return new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltMultipleGeneric, resultNode };
         }
         else
         {
             if (xDefault != null)
             {
                 return new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltXdefault, xDefault };
             }
             else
             {
                 // Everything failed, choose the first item.
                 return new object[] { Com.Adobe.Xmp.Impl.XMPNodeUtils.CltFirstItem, arrayNode.GetChild(1) };
             }
         }
     }
 }
 /// <summary>Moves an alias node of array form to another schema into an array</summary>
 /// <param name="propertyIt">the property iterator of the old schema (used to delete the property)</param>
 /// <param name="childNode">the node to be moved</param>
 /// <param name="baseArray">the base array for the array item</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards XMP errors</exception>
 private static void TransplantArrayItemAlias(Iterator propertyIt, XMPNode childNode, XMPNode baseArray)
 {
     if (baseArray.GetOptions().IsArrayAltText())
     {
         if (childNode.GetOptions().GetHasLanguage())
         {
             throw new XMPException("Alias to x-default already has a language qualifier", XMPErrorConstants.Badxmp);
         }
         XMPNode langQual = new XMPNode(XMPConstConstants.XmlLang, XMPConstConstants.XDefault, null);
         childNode.AddQualifier(langQual);
     }
     propertyIt.Remove();
     childNode.SetName(XMPConstConstants.ArrayItemName);
     baseArray.AddChild(childNode);
 }
		/// <seealso cref="Com.Adobe.Xmp.XMPUtils.AppendProperties(Com.Adobe.Xmp.XMPMeta, Com.Adobe.Xmp.XMPMeta, bool, bool)"/>
		/// <param name="source">The source XMP object.</param>
		/// <param name="destination">The destination XMP object.</param>
		/// <param name="doAllProperties">Do internal properties in addition to external properties.</param>
		/// <param name="replaceOldValues">Replace the values of existing properties.</param>
		/// <param name="deleteEmptyValues">Delete destination values if source property is empty.</param>
		/// <exception cref="Com.Adobe.Xmp.XMPException">Forwards the Exceptions from the metadata processing</exception>
		public static void AppendProperties(XMPMeta source, XMPMeta destination, bool doAllProperties, bool replaceOldValues, bool deleteEmptyValues)
		{
			ParameterAsserts.AssertImplementation(source);
			ParameterAsserts.AssertImplementation(destination);
			XMPMetaImpl src = (XMPMetaImpl)source;
			XMPMetaImpl dest = (XMPMetaImpl)destination;
			for (Iterator it = src.GetRoot().IterateChildren(); it.HasNext(); )
			{
				XMPNode sourceSchema = (XMPNode)it.Next();
				// Make sure we have a destination schema node
				XMPNode destSchema = XMPNodeUtils.FindSchemaNode(dest.GetRoot(), sourceSchema.GetName(), false);
				bool createdSchema = false;
				if (destSchema == null)
				{
					destSchema = new XMPNode(sourceSchema.GetName(), sourceSchema.GetValue(), new PropertyOptions().SetSchemaNode(true));
					dest.GetRoot().AddChild(destSchema);
					createdSchema = true;
				}
				// Process the source schema's children.			
				for (Iterator ic = sourceSchema.IterateChildren(); ic.HasNext(); )
				{
					XMPNode sourceProp = (XMPNode)ic.Next();
					if (doAllProperties || !Utils.IsInternalProperty(sourceSchema.GetName(), sourceProp.GetName()))
					{
						AppendSubtree(dest, sourceProp, destSchema, replaceOldValues, deleteEmptyValues);
					}
				}
				if (!destSchema.HasChildren() && (createdSchema || deleteEmptyValues))
				{
					// Don't create an empty schema / remove empty schema.
					dest.GetRoot().RemoveChild(destSchema);
				}
			}
		}
		/// <summary>
		/// Remove all schema children according to the flag
		/// <code>doAllProperties</code>.
		/// </summary>
		/// <remarks>
		/// Remove all schema children according to the flag
		/// <code>doAllProperties</code>. Empty schemas are automatically remove
		/// by <code>XMPNode</code>
		/// </remarks>
		/// <param name="schemaNode">a schema node</param>
		/// <param name="doAllProperties">flag if all properties or only externals shall be removed.</param>
		/// <returns>Returns true if the schema is empty after the operation.</returns>
		private static bool RemoveSchemaChildren(XMPNode schemaNode, bool doAllProperties)
		{
			for (Iterator it = schemaNode.IterateChildren(); it.HasNext(); )
			{
				XMPNode currProp = (XMPNode)it.Next();
				if (doAllProperties || !Utils.IsInternalProperty(schemaNode.GetName(), currProp.GetName()))
				{
					it.Remove();
				}
			}
			return !schemaNode.HasChildren();
		}
		/// <seealso cref="AppendProperties(Com.Adobe.Xmp.XMPMeta, Com.Adobe.Xmp.XMPMeta, bool, bool, bool)"/>
		/// <param name="destXMP">The destination XMP object.</param>
		/// <param name="sourceNode">the source node</param>
		/// <param name="destParent">the parent of the destination node</param>
		/// <param name="replaceOldValues">Replace the values of existing properties.</param>
		/// <param name="deleteEmptyValues">
		/// flag if properties with empty values should be deleted
		/// in the destination object.
		/// </param>
		/// <exception cref="Com.Adobe.Xmp.XMPException"/>
		private static void AppendSubtree(XMPMetaImpl destXMP, XMPNode sourceNode, XMPNode destParent, bool replaceOldValues, bool deleteEmptyValues)
		{
			XMPNode destNode = XMPNodeUtils.FindChildNode(destParent, sourceNode.GetName(), false);
			bool valueIsEmpty = false;
			if (deleteEmptyValues)
			{
				valueIsEmpty = sourceNode.GetOptions().IsSimple() ? sourceNode.GetValue() == null || sourceNode.GetValue().Length == 0 : !sourceNode.HasChildren();
			}
			if (deleteEmptyValues && valueIsEmpty)
			{
				if (destNode != null)
				{
					destParent.RemoveChild(destNode);
				}
			}
			else
			{
				if (destNode == null)
				{
					// The one easy case, the destination does not exist.
					destParent.AddChild((XMPNode)sourceNode.Clone());
				}
				else
				{
					if (replaceOldValues)
					{
						// The destination exists and should be replaced.
						destXMP.SetNode(destNode, sourceNode.GetValue(), sourceNode.GetOptions(), true);
						destParent.RemoveChild(destNode);
						destNode = (XMPNode)sourceNode.Clone();
						destParent.AddChild(destNode);
					}
					else
					{
						// The destination exists and is not totally replaced. Structs and
						// arrays are merged.
						PropertyOptions sourceForm = sourceNode.GetOptions();
						PropertyOptions destForm = destNode.GetOptions();
						if (sourceForm != destForm)
						{
							return;
						}
						if (sourceForm.IsStruct())
						{
							// To merge a struct process the fields recursively. E.g. add simple missing fields.
							// The recursive call to AppendSubtree will handle deletion for fields with empty 
							// values.
							for (Iterator it = sourceNode.IterateChildren(); it.HasNext(); )
							{
								XMPNode sourceField = (XMPNode)it.Next();
								AppendSubtree(destXMP, sourceField, destNode, replaceOldValues, deleteEmptyValues);
								if (deleteEmptyValues && !destNode.HasChildren())
								{
									destParent.RemoveChild(destNode);
								}
							}
						}
						else
						{
							if (sourceForm.IsArrayAltText())
							{
								// Merge AltText arrays by the "xml:lang" qualifiers. Make sure x-default is first. 
								// Make a special check for deletion of empty values. Meaningful in AltText arrays 
								// because the "xml:lang" qualifier provides unambiguous source/dest correspondence.
								for (Iterator it = sourceNode.IterateChildren(); it.HasNext(); )
								{
									XMPNode sourceItem = (XMPNode)it.Next();
									if (!sourceItem.HasQualifier() || !XMPConstConstants.XmlLang.Equals(sourceItem.GetQualifier(1).GetName()))
									{
										continue;
									}
									int destIndex = XMPNodeUtils.LookupLanguageItem(destNode, sourceItem.GetQualifier(1).GetValue());
									if (deleteEmptyValues && (sourceItem.GetValue() == null || sourceItem.GetValue().Length == 0))
									{
										if (destIndex != -1)
										{
											destNode.RemoveChild(destIndex);
											if (!destNode.HasChildren())
											{
												destParent.RemoveChild(destNode);
											}
										}
									}
									else
									{
										if (destIndex == -1)
										{
											// Not replacing, keep the existing item.						
											if (!XMPConstConstants.XDefault.Equals(sourceItem.GetQualifier(1).GetValue()) || !destNode.HasChildren())
											{
												sourceItem.CloneSubtree(destNode);
											}
											else
											{
												XMPNode destItem = new XMPNode(sourceItem.GetName(), sourceItem.GetValue(), sourceItem.GetOptions());
												sourceItem.CloneSubtree(destItem);
												destNode.AddChild(1, destItem);
											}
										}
									}
								}
							}
							else
							{
								if (sourceForm.IsArray())
								{
									// Merge other arrays by item values. Don't worry about order or duplicates. Source 
									// items with empty values do not cause deletion, that conflicts horribly with 
									// merging.
									for (Iterator @is = sourceNode.IterateChildren(); @is.HasNext(); )
									{
										XMPNode sourceItem = (XMPNode)@is.Next();
										bool match = false;
										for (Iterator id = destNode.IterateChildren(); id.HasNext(); )
										{
											XMPNode destItem = (XMPNode)id.Next();
											if (ItemValuesMatch(sourceItem, destItem))
											{
												match = true;
											}
										}
										if (!match)
										{
											destNode = (XMPNode)sourceItem.Clone();
											destParent.AddChild(destNode);
										}
									}
								}
							}
						}
					}
				}
			}
		}
		/// <summary>
		/// The internals for SetProperty() and related calls, used after the node is
		/// found or created.
		/// </summary>
		/// <param name="node">the newly created node</param>
		/// <param name="value">the node value, can be <code>null</code></param>
		/// <param name="newOptions">options for the new node, must not be <code>null</code>.</param>
		/// <param name="deleteExisting">flag if the existing value is to be overwritten</param>
		/// <exception cref="Com.Adobe.Xmp.XMPException">thrown if options and value do not correspond</exception>
		internal virtual void SetNode(XMPNode node, object value, PropertyOptions newOptions, bool deleteExisting)
		{
			if (deleteExisting)
			{
				node.Clear();
			}
			// its checked by SetOptions(), if the merged result is a valid options set
			node.GetOptions().MergeWith(newOptions);
			if (!node.GetOptions().IsCompositeProperty())
			{
				// This is setting the value of a leaf node.
				XMPNodeUtils.SetNodeValue(node, value);
			}
			else
			{
				if (value != null && value.ToString().Length > 0)
				{
					throw new XMPException("Composite nodes can't have values", XMPErrorConstants.Badxpath);
				}
				node.RemoveChildren();
			}
		}
 /// <summary>Find or create a qualifier node under a given parent node.</summary>
 /// <remarks>
 /// Find or create a qualifier node under a given parent node. Returns a pointer to the
 /// qualifier node, and optionally an iterator for the node's position in
 /// the parent's vector of qualifiers. The iterator is unchanged if no qualifier node (null)
 /// is returned.
 /// <em>Note:</em> On entry, the qualName parameter must not have the leading '?' from the
 /// XMPPath step.
 /// </remarks>
 /// <param name="parent">the parent XMPNode</param>
 /// <param name="qualName">the qualifier name</param>
 /// <param name="createNodes">flag if nodes shall be created</param>
 /// <returns>Returns the qualifier node if found or created, <code>null</code> otherwise.</returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException"></exception>
 private static XMPNode FindQualifierNode(XMPNode parent, string qualName, bool createNodes)
 {
     System.Diagnostics.Debug.Assert(!qualName.StartsWith("?"));
     XMPNode qualNode = parent.FindQualifierByName(qualName);
     if (qualNode == null && createNodes)
     {
         qualNode = new XMPNode(qualName, null);
         qualNode.SetImplicit(true);
         parent.AddQualifier(qualNode);
     }
     return qualNode;
 }
			public _XMPProperty_407(XMPNode itemNode)
			{
				this.itemNode = itemNode;
			}
Example #42
0
        /// <summary>
        /// see
        /// <see cref="Com.Adobe.Xmp.XMPUtils.SeparateArrayItems(Com.Adobe.Xmp.XMPMeta, string, string, string, Com.Adobe.Xmp.Options.PropertyOptions, bool)"/>
        /// </summary>
        /// <param name="xmp">The XMP object containing the array to be updated.</param>
        /// <param name="schemaNS">
        /// The schema namespace URI for the array. Must not be null or
        /// the empty string.
        /// </param>
        /// <param name="arrayName">
        /// The name of the array. May be a general path expression, must
        /// not be null or the empty string. Each item in the array must
        /// be a simple string value.
        /// </param>
        /// <param name="catedStr">The string to be separated into the array items.</param>
        /// <param name="arrayOptions">Option flags to control the separation.</param>
        /// <param name="preserveCommas">Flag if commas shall be preserved</param>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards the Exceptions from the metadata processing</exception>
        public static void SeparateArrayItems(XMPMeta xmp, string schemaNS, string arrayName, string catedStr, PropertyOptions arrayOptions, bool preserveCommas)
        {
            ParameterAsserts.AssertSchemaNS(schemaNS);
            ParameterAsserts.AssertArrayName(arrayName);
            if (catedStr == null)
            {
                throw new XMPException("Parameter must not be null", XMPErrorConstants.Badparam);
            }
            ParameterAsserts.AssertImplementation(xmp);
            XMPMetaImpl xmpImpl = (XMPMetaImpl)xmp;
            // Keep a zero value, has special meaning below.
            XMPNode arrayNode = SeparateFindCreateArray(schemaNS, arrayName, arrayOptions, xmpImpl);
            // Extract the item values one at a time, until the whole input string is done.
            string itemValue;
            int    itemStart;
            int    itemEnd;
            int    nextKind = UckNormal;
            int    charKind = UckNormal;
            char   ch       = (char)0;
            char   nextChar = (char)0;

            itemEnd = 0;
            int endPos = catedStr.Length;

            while (itemEnd < endPos)
            {
                // Skip any leading spaces and separation characters. Always skip commas here.
                // They can be kept when within a value, but not when alone between values.
                for (itemStart = itemEnd; itemStart < endPos; itemStart++)
                {
                    ch       = catedStr[itemStart];
                    charKind = ClassifyCharacter(ch);
                    if (charKind == UckNormal || charKind == UckQuote)
                    {
                        break;
                    }
                }
                if (itemStart >= endPos)
                {
                    break;
                }
                if (charKind != UckQuote)
                {
                    // This is not a quoted value. Scan for the end, create an array
                    // item from the substring.
                    for (itemEnd = itemStart; itemEnd < endPos; itemEnd++)
                    {
                        ch       = catedStr[itemEnd];
                        charKind = ClassifyCharacter(ch);
                        if (charKind == UckNormal || charKind == UckQuote || (charKind == UckComma && preserveCommas))
                        {
                            continue;
                        }
                        else
                        {
                            if (charKind != UckSpace)
                            {
                                break;
                            }
                            else
                            {
                                if ((itemEnd + 1) < endPos)
                                {
                                    ch       = catedStr[itemEnd + 1];
                                    nextKind = ClassifyCharacter(ch);
                                    if (nextKind == UckNormal || nextKind == UckQuote || (nextKind == UckComma && preserveCommas))
                                    {
                                        continue;
                                    }
                                }
                            }
                        }
                        // Anything left?
                        break;
                    }
                    // Have multiple spaces, or a space followed by a
                    // separator.
                    itemValue = Sharpen.Runtime.Substring(catedStr, itemStart, itemEnd);
                }
                else
                {
                    // Accumulate quoted values into a local string, undoubling
                    // internal quotes that
                    // match the surrounding quotes. Do not undouble "unmatching"
                    // quotes.
                    char openQuote  = ch;
                    char closeQuote = GetClosingQuote(openQuote);
                    itemStart++;
                    // Skip the opening quote;
                    itemValue = string.Empty;
                    for (itemEnd = itemStart; itemEnd < endPos; itemEnd++)
                    {
                        ch       = catedStr[itemEnd];
                        charKind = ClassifyCharacter(ch);
                        if (charKind != UckQuote || !IsSurroundingQuote(ch, openQuote, closeQuote))
                        {
                            // This is not a matching quote, just append it to the
                            // item value.
                            itemValue += ch;
                        }
                        else
                        {
                            // This is a "matching" quote. Is it doubled, or the
                            // final closing quote?
                            // Tolerate various edge cases like undoubled opening
                            // (non-closing) quotes,
                            // or end of input.
                            if ((itemEnd + 1) < endPos)
                            {
                                nextChar = catedStr[itemEnd + 1];
                                nextKind = ClassifyCharacter(nextChar);
                            }
                            else
                            {
                                nextKind = UckSemicolon;
                                nextChar = (char)unchecked ((int)(0x3B));
                            }
                            if (ch == nextChar)
                            {
                                // This is doubled, copy it and skip the double.
                                itemValue += ch;
                                // Loop will add in charSize.
                                itemEnd++;
                            }
                            else
                            {
                                if (!IsClosingingQuote(ch, openQuote, closeQuote))
                                {
                                    // This is an undoubled, non-closing quote, copy it.
                                    itemValue += ch;
                                }
                                else
                                {
                                    // This is an undoubled closing quote, skip it and
                                    // exit the loop.
                                    itemEnd++;
                                    break;
                                }
                            }
                        }
                    }
                }
                // Add the separated item to the array.
                // Keep a matching old value in case it had separators.
                int foundIndex = -1;
                for (int oldChild = 1; oldChild <= arrayNode.GetChildrenLength(); oldChild++)
                {
                    if (itemValue.Equals(arrayNode.GetChild(oldChild).GetValue()))
                    {
                        foundIndex = oldChild;
                        break;
                    }
                }
                XMPNode newItem = null;
                if (foundIndex < 0)
                {
                    newItem = new XMPNode(XMPConstConstants.ArrayItemName, itemValue, null);
                    arrayNode.AddChild(newItem);
                }
            }
        }
			public _XMPProperty_682(object value, XMPNode propNode)
			{
				this.value = value;
				this.propNode = propNode;
			}
 /// <param name="arrayNode">an array node</param>
 /// <param name="segment">the segment containing the array index</param>
 /// <param name="createNodes">flag if new nodes are allowed to be created.</param>
 /// <returns>Returns the index or index = -1 if not found</returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Throws Exceptions</exception>
 private static int FindIndexedItem(XMPNode arrayNode, string segment, bool createNodes)
 {
     int index = 0;
     try
     {
         segment = Sharpen.Runtime.Substring(segment, 1, segment.Length - 1);
         index = System.Convert.ToInt32(segment);
         if (index < 1)
         {
             throw new XMPException("Array index must be larger than zero", XMPErrorConstants.Badxpath);
         }
     }
     catch (FormatException)
     {
         throw new XMPException("Array index not digits.", XMPErrorConstants.Badxpath);
     }
     if (createNodes && index == arrayNode.GetChildrenLength() + 1)
     {
         // Append a new last + 1 node.
         XMPNode newItem = new XMPNode(XMPConstConstants.ArrayItemName, null);
         newItem.SetImplicit(true);
         arrayNode.AddChild(newItem);
     }
     return index;
 }
Example #45
0
        /// <summary>Follow an expanded path expression to find or create a node.</summary>
        /// <param name="xmpTree">the node to begin the search.</param>
        /// <param name="xpath">the complete xpath</param>
        /// <param name="createNodes">
        /// flag if nodes shall be created
        /// (when called by <code>setProperty()</code>)
        /// </param>
        /// <param name="leafOptions">
        /// the options for the created leaf nodes (only when
        /// <code>createNodes == true</code>).
        /// </param>
        /// <returns>Returns the node if found or created or <code>null</code>.</returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException">
        /// An exception is only thrown if an error occurred,
        /// not if a node was not found.
        /// </exception>
        internal static XMPNode FindNode(XMPNode xmpTree, XMPPath xpath, bool createNodes, PropertyOptions leafOptions)
        {
            // check if xpath is set.
            if (xpath == null || xpath.Size() == 0)
            {
                throw new XMPException("Empty XMPPath", XMPErrorConstants.Badxpath);
            }
            // Root of implicitly created subtree to possible delete it later.
            // Valid only if leaf is new.
            XMPNode rootImplicitNode = null;
            XMPNode currNode         = null;

            // resolve schema step
            currNode = FindSchemaNode(xmpTree, xpath.GetSegment(XMPPath.StepSchema).GetName(), createNodes);
            if (currNode == null)
            {
                return(null);
            }
            else
            {
                if (currNode.IsImplicit())
                {
                    currNode.SetImplicit(false);
                    // Clear the implicit node bit.
                    rootImplicitNode = currNode;
                }
            }
            // Save the top most implicit node.
            // Now follow the remaining steps of the original XMPPath.
            try
            {
                for (int i = 1; i < xpath.Size(); i++)
                {
                    currNode = FollowXPathStep(currNode, xpath.GetSegment(i), createNodes);
                    if (currNode == null)
                    {
                        if (createNodes)
                        {
                            // delete implicitly created nodes
                            DeleteNode(rootImplicitNode);
                        }
                        return(null);
                    }
                    else
                    {
                        if (currNode.IsImplicit())
                        {
                            // clear the implicit node flag
                            currNode.SetImplicit(false);
                            // if node is an ALIAS (can be only in root step, auto-create array
                            // when the path has been resolved from a not simple alias type
                            if (i == 1 && xpath.GetSegment(i).IsAlias() && xpath.GetSegment(i).GetAliasForm() != 0)
                            {
                                currNode.GetOptions().SetOption(xpath.GetSegment(i).GetAliasForm(), true);
                            }
                            else
                            {
                                // "CheckImplicitStruct" in C++
                                if (i < xpath.Size() - 1 && xpath.GetSegment(i).GetKind() == XMPPath.StructFieldStep && !currNode.GetOptions().IsCompositeProperty())
                                {
                                    currNode.GetOptions().SetStruct(true);
                                }
                            }
                            if (rootImplicitNode == null)
                            {
                                rootImplicitNode = currNode;
                            }
                        }
                    }
                }
            }
            catch (XMPException e)
            {
                // Save the top most implicit node.
                // if new notes have been created prior to the error, delete them
                if (rootImplicitNode != null)
                {
                    DeleteNode(rootImplicitNode);
                }
                throw;
            }
            if (rootImplicitNode != null)
            {
                // set options only if a node has been successful created
                currNode.GetOptions().MergeWith(leafOptions);
                currNode.SetOptions(currNode.GetOptions());
            }
            return(currNode);
        }
Example #46
0
        /// <seealso cref="Com.Adobe.Xmp.XMPUtils.RemoveProperties(Com.Adobe.Xmp.XMPMeta, string, string, bool, bool)"/>
        /// <param name="xmp">The XMP object containing the properties to be removed.</param>
        /// <param name="schemaNS">
        /// Optional schema namespace URI for the properties to be
        /// removed.
        /// </param>
        /// <param name="propName">Optional path expression for the property to be removed.</param>
        /// <param name="doAllProperties">
        /// Option flag to control the deletion: do internal properties in
        /// addition to external properties.
        /// </param>
        /// <param name="includeAliases">
        /// Option flag to control the deletion: Include aliases in the
        /// "named schema" case above.
        /// </param>
        /// <exception cref="Com.Adobe.Xmp.XMPException">If metadata processing fails</exception>
        public static void RemoveProperties(XMPMeta xmp, string schemaNS, string propName, bool doAllProperties, bool includeAliases)
        {
            ParameterAsserts.AssertImplementation(xmp);
            XMPMetaImpl xmpImpl = (XMPMetaImpl)xmp;

            if (propName != null && propName.Length > 0)
            {
                // Remove just the one indicated property. This might be an alias,
                // the named schema might not actually exist. So don't lookup the
                // schema node.
                if (schemaNS == null || schemaNS.Length == 0)
                {
                    throw new XMPException("Property name requires schema namespace", XMPErrorConstants.Badparam);
                }
                XMPPath expPath  = XMPPathParser.ExpandXPath(schemaNS, propName);
                XMPNode propNode = XMPNodeUtils.FindNode(xmpImpl.GetRoot(), expPath, false, null);
                if (propNode != null)
                {
                    if (doAllProperties || !Utils.IsInternalProperty(expPath.GetSegment(XMPPath.StepSchema).GetName(), expPath.GetSegment(XMPPath.StepRootProp).GetName()))
                    {
                        XMPNode parent = propNode.GetParent();
                        parent.RemoveChild(propNode);
                        if (parent.GetOptions().IsSchemaNode() && !parent.HasChildren())
                        {
                            // remove empty schema node
                            parent.GetParent().RemoveChild(parent);
                        }
                    }
                }
            }
            else
            {
                if (schemaNS != null && schemaNS.Length > 0)
                {
                    // Remove all properties from the named schema. Optionally include
                    // aliases, in which case
                    // there might not be an actual schema node.
                    // XMP_NodePtrPos schemaPos;
                    XMPNode schemaNode = XMPNodeUtils.FindSchemaNode(xmpImpl.GetRoot(), schemaNS, false);
                    if (schemaNode != null)
                    {
                        if (RemoveSchemaChildren(schemaNode, doAllProperties))
                        {
                            xmpImpl.GetRoot().RemoveChild(schemaNode);
                        }
                    }
                    if (includeAliases)
                    {
                        // We're removing the aliases also. Look them up by their
                        // namespace prefix.
                        // But that takes more code and the extra speed isn't worth it.
                        // Lookup the XMP node
                        // from the alias, to make sure the actual exists.
                        XMPAliasInfo[] aliases = XMPMetaFactory.GetSchemaRegistry().FindAliases(schemaNS);
                        for (int i = 0; i < aliases.Length; i++)
                        {
                            XMPAliasInfo info       = aliases[i];
                            XMPPath      path       = XMPPathParser.ExpandXPath(info.GetNamespace(), info.GetPropName());
                            XMPNode      actualProp = XMPNodeUtils.FindNode(xmpImpl.GetRoot(), path, false, null);
                            if (actualProp != null)
                            {
                                XMPNode parent = actualProp.GetParent();
                                parent.RemoveChild(actualProp);
                            }
                        }
                    }
                }
                else
                {
                    // Remove all appropriate properties from all schema. In this case
                    // we don't have to be
                    // concerned with aliases, they are handled implicitly from the
                    // actual properties.
                    for (Iterator it = xmpImpl.GetRoot().IterateChildren(); it.HasNext();)
                    {
                        XMPNode schema = (XMPNode)it.Next();
                        if (RemoveSchemaChildren(schema, doAllProperties))
                        {
                            it.Remove();
                        }
                    }
                }
            }
        }
Example #47
0
 // EMPTY
 /// <summary>Find or create a schema node if <code>createNodes</code> is false and</summary>
 /// <param name="tree">the root of the xmp tree.</param>
 /// <param name="namespaceURI">a namespace</param>
 /// <param name="createNodes">
 /// a flag indicating if the node shall be created if not found.
 /// <em>Note:</em> The namespace must be registered prior to this call.
 /// </param>
 /// <returns>
 /// Returns the schema node if found, <code>null</code> otherwise.
 /// Note: If <code>createNodes</code> is <code>true</code>, it is <b>always</b>
 /// returned a valid node.
 /// </returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException">
 /// An exception is only thrown if an error occurred, not if a
 /// node was not found.
 /// </exception>
 internal static XMPNode FindSchemaNode(XMPNode tree, string namespaceURI, bool createNodes)
 {
     return(FindSchemaNode(tree, namespaceURI, null, createNodes));
 }
Example #48
0
        /// <seealso cref="AppendProperties(Com.Adobe.Xmp.XMPMeta, Com.Adobe.Xmp.XMPMeta, bool, bool, bool)"/>
        /// <param name="destXMP">The destination XMP object.</param>
        /// <param name="sourceNode">the source node</param>
        /// <param name="destParent">the parent of the destination node</param>
        /// <param name="replaceOldValues">Replace the values of existing properties.</param>
        /// <param name="deleteEmptyValues">
        /// flag if properties with empty values should be deleted
        /// in the destination object.
        /// </param>
        /// <exception cref="Com.Adobe.Xmp.XMPException"/>
        private static void AppendSubtree(XMPMetaImpl destXMP, XMPNode sourceNode, XMPNode destParent, bool replaceOldValues, bool deleteEmptyValues)
        {
            XMPNode destNode     = XMPNodeUtils.FindChildNode(destParent, sourceNode.GetName(), false);
            bool    valueIsEmpty = false;

            if (deleteEmptyValues)
            {
                valueIsEmpty = sourceNode.GetOptions().IsSimple() ? sourceNode.GetValue() == null || sourceNode.GetValue().Length == 0 : !sourceNode.HasChildren();
            }
            if (deleteEmptyValues && valueIsEmpty)
            {
                if (destNode != null)
                {
                    destParent.RemoveChild(destNode);
                }
            }
            else
            {
                if (destNode == null)
                {
                    // The one easy case, the destination does not exist.
                    destParent.AddChild((XMPNode)sourceNode.Clone());
                }
                else
                {
                    if (replaceOldValues)
                    {
                        // The destination exists and should be replaced.
                        destXMP.SetNode(destNode, sourceNode.GetValue(), sourceNode.GetOptions(), true);
                        destParent.RemoveChild(destNode);
                        destNode = (XMPNode)sourceNode.Clone();
                        destParent.AddChild(destNode);
                    }
                    else
                    {
                        // The destination exists and is not totally replaced. Structs and
                        // arrays are merged.
                        PropertyOptions sourceForm = sourceNode.GetOptions();
                        PropertyOptions destForm   = destNode.GetOptions();
                        if (sourceForm != destForm)
                        {
                            return;
                        }
                        if (sourceForm.IsStruct())
                        {
                            // To merge a struct process the fields recursively. E.g. add simple missing fields.
                            // The recursive call to AppendSubtree will handle deletion for fields with empty
                            // values.
                            for (Iterator it = sourceNode.IterateChildren(); it.HasNext();)
                            {
                                XMPNode sourceField = (XMPNode)it.Next();
                                AppendSubtree(destXMP, sourceField, destNode, replaceOldValues, deleteEmptyValues);
                                if (deleteEmptyValues && !destNode.HasChildren())
                                {
                                    destParent.RemoveChild(destNode);
                                }
                            }
                        }
                        else
                        {
                            if (sourceForm.IsArrayAltText())
                            {
                                // Merge AltText arrays by the "xml:lang" qualifiers. Make sure x-default is first.
                                // Make a special check for deletion of empty values. Meaningful in AltText arrays
                                // because the "xml:lang" qualifier provides unambiguous source/dest correspondence.
                                for (Iterator it = sourceNode.IterateChildren(); it.HasNext();)
                                {
                                    XMPNode sourceItem = (XMPNode)it.Next();
                                    if (!sourceItem.HasQualifier() || !XMPConstConstants.XmlLang.Equals(sourceItem.GetQualifier(1).GetName()))
                                    {
                                        continue;
                                    }
                                    int destIndex = XMPNodeUtils.LookupLanguageItem(destNode, sourceItem.GetQualifier(1).GetValue());
                                    if (deleteEmptyValues && (sourceItem.GetValue() == null || sourceItem.GetValue().Length == 0))
                                    {
                                        if (destIndex != -1)
                                        {
                                            destNode.RemoveChild(destIndex);
                                            if (!destNode.HasChildren())
                                            {
                                                destParent.RemoveChild(destNode);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (destIndex == -1)
                                        {
                                            // Not replacing, keep the existing item.
                                            if (!XMPConstConstants.XDefault.Equals(sourceItem.GetQualifier(1).GetValue()) || !destNode.HasChildren())
                                            {
                                                sourceItem.CloneSubtree(destNode);
                                            }
                                            else
                                            {
                                                XMPNode destItem = new XMPNode(sourceItem.GetName(), sourceItem.GetValue(), sourceItem.GetOptions());
                                                sourceItem.CloneSubtree(destItem);
                                                destNode.AddChild(1, destItem);
                                            }
                                        }
                                    }
                                }
                            }
                            else
                            {
                                if (sourceForm.IsArray())
                                {
                                    // Merge other arrays by item values. Don't worry about order or duplicates. Source
                                    // items with empty values do not cause deletion, that conflicts horribly with
                                    // merging.
                                    for (Iterator @is = sourceNode.IterateChildren(); @is.HasNext();)
                                    {
                                        XMPNode sourceItem = (XMPNode)@is.Next();
                                        bool    match      = false;
                                        for (Iterator id = destNode.IterateChildren(); id.HasNext();)
                                        {
                                            XMPNode destItem = (XMPNode)id.Next();
                                            if (ItemValuesMatch(sourceItem, destItem))
                                            {
                                                match = true;
                                            }
                                        }
                                        if (!match)
                                        {
                                            destNode = (XMPNode)sourceItem.Clone();
                                            destParent.AddChild(destNode);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
Example #49
0
            /// <summary>Creates a property info object from an <code>XMPNode</code>.</summary>
            /// <param name="node">an <code>XMPNode</code></param>
            /// <param name="baseNS">the base namespace to report</param>
            /// <param name="path">the full property path</param>
            /// <returns>Returns a <code>XMPProperty</code>-object that serves representation of the node.</returns>
            protected internal virtual XMPPropertyInfo CreatePropertyInfo(XMPNode node, string baseNS, string path)
            {
                string value = node.GetOptions().IsSchemaNode() ? null : node.GetValue();

                return(new _XMPPropertyInfo_450(node, baseNS, path, value));
            }
Example #50
0
        /// <summary>Compares two nodes including its children and qualifier.</summary>
        /// <param name="leftNode">an <code>XMPNode</code></param>
        /// <param name="rightNode">an <code>XMPNode</code></param>
        /// <returns>Returns true if the nodes are equal, false otherwise.</returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards exceptions to the calling method.</exception>
        private static bool ItemValuesMatch(XMPNode leftNode, XMPNode rightNode)
        {
            PropertyOptions leftForm  = leftNode.GetOptions();
            PropertyOptions rightForm = rightNode.GetOptions();

            if (leftForm.Equals(rightForm))
            {
                return(false);
            }
            if (leftForm.GetOptions() == 0)
            {
                // Simple nodes, check the values and xml:lang qualifiers.
                if (!leftNode.GetValue().Equals(rightNode.GetValue()))
                {
                    return(false);
                }
                if (leftNode.GetOptions().GetHasLanguage() != rightNode.GetOptions().GetHasLanguage())
                {
                    return(false);
                }
                if (leftNode.GetOptions().GetHasLanguage() && !leftNode.GetQualifier(1).GetValue().Equals(rightNode.GetQualifier(1).GetValue()))
                {
                    return(false);
                }
            }
            else
            {
                if (leftForm.IsStruct())
                {
                    // Struct nodes, see if all fields match, ignoring order.
                    if (leftNode.GetChildrenLength() != rightNode.GetChildrenLength())
                    {
                        return(false);
                    }
                    for (Iterator it = leftNode.IterateChildren(); it.HasNext();)
                    {
                        XMPNode leftField  = (XMPNode)it.Next();
                        XMPNode rightField = XMPNodeUtils.FindChildNode(rightNode, leftField.GetName(), false);
                        if (rightField == null || !ItemValuesMatch(leftField, rightField))
                        {
                            return(false);
                        }
                    }
                }
                else
                {
                    // Array nodes, see if the "leftNode" values are present in the
                    // "rightNode", ignoring order, duplicates,
                    // and extra values in the rightNode-> The rightNode is the
                    // destination for AppendProperties.
                    System.Diagnostics.Debug.Assert(leftForm.IsArray());
                    for (Iterator il = leftNode.IterateChildren(); il.HasNext();)
                    {
                        XMPNode leftItem = (XMPNode)il.Next();
                        bool    match    = false;
                        for (Iterator ir = rightNode.IterateChildren(); ir.HasNext();)
                        {
                            XMPNode rightItem = (XMPNode)ir.Next();
                            if (ItemValuesMatch(leftItem, rightItem))
                            {
                                match = true;
                                break;
                            }
                        }
                        if (!match)
                        {
                            return(false);
                        }
                    }
                }
            }
            return(true);
        }
		/// <summary>Compares two nodes including its children and qualifier.</summary>
		/// <param name="leftNode">an <code>XMPNode</code></param>
		/// <param name="rightNode">an <code>XMPNode</code></param>
		/// <returns>Returns true if the nodes are equal, false otherwise.</returns>
		/// <exception cref="Com.Adobe.Xmp.XMPException">Forwards exceptions to the calling method.</exception>
		private static bool ItemValuesMatch(XMPNode leftNode, XMPNode rightNode)
		{
			PropertyOptions leftForm = leftNode.GetOptions();
			PropertyOptions rightForm = rightNode.GetOptions();
			if (leftForm.Equals(rightForm))
			{
				return false;
			}
			if (leftForm.GetOptions() == 0)
			{
				// Simple nodes, check the values and xml:lang qualifiers.
				if (!leftNode.GetValue().Equals(rightNode.GetValue()))
				{
					return false;
				}
				if (leftNode.GetOptions().GetHasLanguage() != rightNode.GetOptions().GetHasLanguage())
				{
					return false;
				}
				if (leftNode.GetOptions().GetHasLanguage() && !leftNode.GetQualifier(1).GetValue().Equals(rightNode.GetQualifier(1).GetValue()))
				{
					return false;
				}
			}
			else
			{
				if (leftForm.IsStruct())
				{
					// Struct nodes, see if all fields match, ignoring order.
					if (leftNode.GetChildrenLength() != rightNode.GetChildrenLength())
					{
						return false;
					}
					for (Iterator it = leftNode.IterateChildren(); it.HasNext(); )
					{
						XMPNode leftField = (XMPNode)it.Next();
						XMPNode rightField = XMPNodeUtils.FindChildNode(rightNode, leftField.GetName(), false);
						if (rightField == null || !ItemValuesMatch(leftField, rightField))
						{
							return false;
						}
					}
				}
				else
				{
					// Array nodes, see if the "leftNode" values are present in the
					// "rightNode", ignoring order, duplicates,
					// and extra values in the rightNode-> The rightNode is the
					// destination for AppendProperties.
					System.Diagnostics.Debug.Assert(leftForm.IsArray());
					for (Iterator il = leftNode.IterateChildren(); il.HasNext(); )
					{
						XMPNode leftItem = (XMPNode)il.Next();
						bool match = false;
						for (Iterator ir = rightNode.IterateChildren(); ir.HasNext(); )
						{
							XMPNode rightItem = (XMPNode)ir.Next();
							if (ItemValuesMatch(leftItem, rightItem))
							{
								match = true;
								break;
							}
						}
						if (!match)
						{
							return false;
						}
					}
				}
			}
			return true;
		}
 /// <summary>
 /// After processing by ExpandXPath, a step can be of these forms:
 /// <ul>
 /// <li>qualName - A top level property or struct field.
 /// </summary>
 /// <remarks>
 /// After processing by ExpandXPath, a step can be of these forms:
 /// <ul>
 /// <li>qualName - A top level property or struct field.
 /// <li>[index] - An element of an array.
 /// <li>[last()] - The last element of an array.
 /// <li>[qualName="value"] - An element in an array of structs, chosen by a field value.
 /// <li>[?qualName="value"] - An element in an array, chosen by a qualifier value.
 /// <li>?qualName - A general qualifier.
 /// </ul>
 /// Find the appropriate child node, resolving aliases, and optionally creating nodes.
 /// </remarks>
 /// <param name="parentNode">the node to start to start from</param>
 /// <param name="nextStep">the xpath segment</param>
 /// <param name="createNodes"></param>
 /// <returns>returns the found or created XMPPath node</returns>
 /// <exception cref="Com.Adobe.Xmp.XMPException"></exception>
 private static XMPNode FollowXPathStep(XMPNode parentNode, XMPPathSegment nextStep, bool createNodes)
 {
     XMPNode nextNode = null;
     int index = 0;
     int stepKind = nextStep.GetKind();
     if (stepKind == XMPPath.StructFieldStep)
     {
         nextNode = FindChildNode(parentNode, nextStep.GetName(), createNodes);
     }
     else
     {
         if (stepKind == XMPPath.QualifierStep)
         {
             nextNode = FindQualifierNode(parentNode, Sharpen.Runtime.Substring(nextStep.GetName(), 1), createNodes);
         }
         else
         {
             // This is an array indexing step. First get the index, then get the node.
             if (!parentNode.GetOptions().IsArray())
             {
                 throw new XMPException("Indexing applied to non-array", XMPErrorConstants.Badxpath);
             }
             if (stepKind == XMPPath.ArrayIndexStep)
             {
                 index = FindIndexedItem(parentNode, nextStep.GetName(), createNodes);
             }
             else
             {
                 if (stepKind == XMPPath.ArrayLastStep)
                 {
                     index = parentNode.GetChildrenLength();
                 }
                 else
                 {
                     if (stepKind == XMPPath.FieldSelectorStep)
                     {
                         string[] result = Utils.SplitNameAndValue(nextStep.GetName());
                         string fieldName = result[0];
                         string fieldValue = result[1];
                         index = LookupFieldSelector(parentNode, fieldName, fieldValue);
                     }
                     else
                     {
                         if (stepKind == XMPPath.QualSelectorStep)
                         {
                             string[] result = Utils.SplitNameAndValue(nextStep.GetName());
                             string qualName = result[0];
                             string qualValue = result[1];
                             index = LookupQualSelector(parentNode, qualName, qualValue, nextStep.GetAliasForm());
                         }
                         else
                         {
                             throw new XMPException("Unknown array indexing step in FollowXPathStep", XMPErrorConstants.Internalfailure);
                         }
                     }
                 }
             }
             if (1 <= index && index <= parentNode.GetChildrenLength())
             {
                 nextNode = parentNode.GetChild(index);
             }
         }
     }
     return nextNode;
 }