// EMPTY
        /// <summary>
        /// Parses the input source into an XMP metadata object, including
        /// de-aliasing and normalisation.
        /// </summary>
        /// <param name="input">
        /// the input can be an <code>InputStream</code>, a <code>String</code> or
        /// a byte buffer containing the XMP packet.
        /// </param>
        /// <param name="options">the parse options</param>
        /// <returns>Returns the resulting XMP metadata object</returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if parsing or normalisation fails.</exception>
        public static XMPMeta Parse(object input, ParseOptions options)
        {
            ParameterAsserts.AssertNotNull(input);
            options = options != null ? options : new ParseOptions();
            XmlDocument document        = ParseXml(input, options);
            bool        xmpmetaRequired = options.GetRequireXMPMeta();

            object[] result = new object[3];
            result = FindRootNode(document, xmpmetaRequired, result);
            if (result != null && result[1] == XmpRdf)
            {
                XMPMetaImpl xmp = ParseRDF.Parse((XmlNode)result[0]);
                xmp.SetPacketHeader((string)result[2]);
                // Check if the XMP object shall be normalized
                if (!options.GetOmitNormalization())
                {
                    return(XMPNormalizer.Process(xmp, options));
                }
                else
                {
                    return(xmp);
                }
            }
            else
            {
                // no appropriate root node found, return empty metadata object
                return(new XMPMetaImpl());
            }
        }
Beispiel #2
0
        /// <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>Serializes an <code>XMPMeta</code>-object as RDF into a byte buffer.</summary>
        /// <param name="xmp">a metadata implementation object</param>
        /// <param name="options">
        /// Options to control the serialization (see
        /// <see cref="Com.Adobe.Xmp.Options.SerializeOptions"/>
        /// ).
        /// </param>
        /// <returns>Returns a byte buffer containing the serialized RDF.</returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException">on serializsation errors.</exception>
        public static sbyte[] SerializeToBuffer(XMPMetaImpl xmp, SerializeOptions options)
        {
            ByteArrayOutputStream @out = new ByteArrayOutputStream(2048);

            Serialize(xmp, @out, options);
            return(@out.ToByteArray());
        }
Beispiel #4
0
        // EMPTY
        /// <seealso cref="Com.Adobe.Xmp.XMPUtils.CatenateArrayItems(Com.Adobe.Xmp.XMPMeta, string, string, string, string, bool)"/>
        /// <param name="xmp">The XMP object containing the array to be catenated.</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="separator">
        /// The string to be used to separate the items in the catenated
        /// string. Defaults to &quot;; &quot;, ASCII semicolon and space
        /// (U+003B, U+0020).
        /// </param>
        /// <param name="quotes">
        /// The characters to be used as quotes around array items that
        /// contain a separator. Defaults to &apos;&quot;&apos;
        /// </param>
        /// <param name="allowCommas">Option flag to control the catenation.</param>
        /// <returns>Returns the string containing the catenated array items.</returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Forwards the Exceptions from the metadata processing</exception>
        public static string CatenateArrayItems(XMPMeta xmp, string schemaNS, string arrayName, string separator, string quotes, bool allowCommas)
        {
            ParameterAsserts.AssertSchemaNS(schemaNS);
            ParameterAsserts.AssertArrayName(arrayName);
            ParameterAsserts.AssertImplementation(xmp);
            if (separator == null || separator.Length == 0)
            {
                separator = "; ";
            }
            if (quotes == null || quotes.Length == 0)
            {
                quotes = "\"";
            }
            XMPMetaImpl xmpImpl   = (XMPMetaImpl)xmp;
            XMPNode     arrayNode = null;
            XMPNode     currItem  = null;
            // Return an empty result if the array does not exist,
            // hurl if it isn't the right form.
            XMPPath arrayPath = XMPPathParser.ExpandXPath(schemaNS, arrayName);

            arrayNode = XMPNodeUtils.FindNode(xmpImpl.GetRoot(), arrayPath, false, null);
            if (arrayNode == null)
            {
                return(string.Empty);
            }
            else
            {
                if (!arrayNode.GetOptions().IsArray() || arrayNode.GetOptions().IsArrayAlternate())
                {
                    throw new XMPException("Named property must be non-alternate array", XMPErrorConstants.Badparam);
                }
            }
            // Make sure the separator is OK.
            CheckSeparator(separator);
            // Make sure the open and close quotes are a legitimate pair.
            char openQuote  = quotes[0];
            char closeQuote = CheckQuotes(quotes, openQuote);
            // Build the result, quoting the array items, adding separators.
            // Hurl if any item isn't simple.
            StringBuilder catinatedString = new StringBuilder();

            for (Iterator it = arrayNode.IterateChildren(); it.HasNext();)
            {
                currItem = (XMPNode)it.Next();
                if (currItem.GetOptions().IsCompositeProperty())
                {
                    throw new XMPException("Array items must be simple", XMPErrorConstants.Badparam);
                }
                string str = ApplyQuotes(currItem.GetValue(), openQuote, closeQuote, allowCommas);
                catinatedString.Append(str);
                if (it.HasNext())
                {
                    catinatedString.Append(separator);
                }
            }
            return(catinatedString.ToString());
        }
        // EMPTY
        /// <summary>Normalizes a raw parsed XMPMeta-Object</summary>
        /// <param name="xmp">the raw metadata object</param>
        /// <param name="options">the parsing options</param>
        /// <returns>Returns the normalized metadata object</returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException">Collects all severe processing errors.</exception>
        internal static XMPMeta Process(XMPMetaImpl xmp, ParseOptions options)
        {
            XMPNode tree = xmp.GetRoot();

            TouchUpDataModel(xmp);
            MoveExplicitAliases(tree, options);
            TweakOldXMP(tree);
            DeleteEmptySchemas(tree);
            return(xmp);
        }
 /// <summary>Static method to serialize the metadata object.</summary>
 /// <remarks>
 /// Static method to serialize the metadata object. For each serialisation, a new XMPSerializer
 /// instance is created, either XMPSerializerRDF or XMPSerializerPlain so thats its possible to
 /// serialialize the same XMPMeta objects in two threads.
 /// </remarks>
 /// <param name="xmp">a metadata implementation object</param>
 /// <param name="out">the output stream to serialize to</param>
 /// <param name="options">serialization options, can be <code>null</code> for default.</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException"/>
 public static void Serialize(XMPMetaImpl xmp, OutputStream @out, SerializeOptions options)
 {
     options = options != null ? options : new SerializeOptions();
     // sort the internal data model on demand
     if (options.GetSort())
     {
         xmp.Sort();
     }
     new XMPSerializerRDF().Serialize(xmp, @out, options);
 }
 /// <summary>Visit all schemas to do general fixes and handle special cases.</summary>
 /// <param name="xmp">the metadata object implementation</param>
 /// <exception cref="Com.Adobe.Xmp.XMPException">Thrown if the normalisation fails.</exception>
 private static void TouchUpDataModel(XMPMetaImpl xmp)
 {
     // make sure the DC schema is existing, because it might be needed within the normalization
     // if not touched it will be removed by removeEmptySchemas
     XMPNodeUtils.FindSchemaNode(xmp.GetRoot(), XMPConstConstants.NsDc, true);
     // Do the special case fixes within each schema.
     for (Iterator it = xmp.GetRoot().IterateChildren(); it.HasNext();)
     {
         XMPNode currSchema = (XMPNode)it.Next();
         if (XMPConstConstants.NsDc.Equals(currSchema.GetName()))
         {
             NormalizeDCArrays(currSchema);
         }
         else
         {
             if (XMPConstConstants.NsExif.Equals(currSchema.GetName()))
             {
                 // Do a special case fix for exif:GPSTimeStamp.
                 FixGPSTimeStamp(currSchema);
                 XMPNode arrayNode = XMPNodeUtils.FindChildNode(currSchema, "exif:UserComment", false);
                 if (arrayNode != null)
                 {
                     RepairAltText(arrayNode);
                 }
             }
             else
             {
                 if (XMPConstConstants.NsDm.Equals(currSchema.GetName()))
                 {
                     // Do a special case migration of xmpDM:copyright to
                     // dc:rights['x-default'].
                     XMPNode dmCopyright = XMPNodeUtils.FindChildNode(currSchema, "xmpDM:copyright", false);
                     if (dmCopyright != null)
                     {
                         MigrateAudioCopyright(xmp, dmCopyright);
                     }
                 }
                 else
                 {
                     if (XMPConstConstants.NsXmpRights.Equals(currSchema.GetName()))
                     {
                         XMPNode arrayNode = XMPNodeUtils.FindChildNode(currSchema, "xmpRights:UsageTerms", false);
                         if (arrayNode != null)
                         {
                             RepairAltText(arrayNode);
                         }
                     }
                 }
             }
         }
     }
 }
        /// <summary>Serializes an <code>XMPMeta</code>-object as RDF into a string.</summary>
        /// <remarks>
        /// Serializes an <code>XMPMeta</code>-object as RDF into a string.
        /// <em>Note:</em> Encoding is forced to UTF-16 when serializing to a
        /// string to ensure the correctness of &quot;exact packet size&quot;.
        /// </remarks>
        /// <param name="xmp">a metadata implementation object</param>
        /// <param name="options">
        /// Options to control the serialization (see
        /// <see cref="Com.Adobe.Xmp.Options.SerializeOptions"/>
        /// ).
        /// </param>
        /// <returns>Returns a string containing the serialized RDF.</returns>
        /// <exception cref="Com.Adobe.Xmp.XMPException">on serializsation errors.</exception>
        public static string SerializeToString(XMPMetaImpl xmp, SerializeOptions options)
        {
            // forces the encoding to be UTF-16 to get the correct string length
            options = options != null ? options : new SerializeOptions();
            options.SetEncodeUTF16BE(true);
            ByteArrayOutputStream @out = new ByteArrayOutputStream(2048);

            Serialize(xmp, @out, options);
            try
            {
                return(@out.ToString(options.GetEncoding()));
            }
            catch (UnsupportedEncodingException)
            {
                // cannot happen as UTF-8/16LE/BE is required to be implemented in
                // Java
                return(@out.ToString());
            }
        }
Beispiel #9
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);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
Beispiel #10
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();
                        }
                    }
                }
            }
        }
Beispiel #11
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);
        }
Beispiel #12
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);
                }
            }
        }
Beispiel #13
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();
            }
        }