/// <summary> /// Constructor with optionsl initial values. If <code>propName</code> is provided, /// <code>schemaNs</code> has also be provided. </summary> /// <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 <seealso cref="IteratorOptions"/> </param> /// <exception cref="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 _options = options ?? new IteratorOptions(); // the start node of the iteration depending on the schema and property filter XmpNode startNode; string initialPath = null; bool baseSchema = !String.IsNullOrEmpty(schemaNs); bool baseProperty = !String.IsNullOrEmpty(propPath); if (!baseSchema && !baseProperty) { // complete tree will be iterated startNode = xmp.Root; } 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.Root, path, false, null); _baseNs = schemaNs; initialPath = basePath.ToString(); } else if (baseSchema && !baseProperty) { // Only Schema provided startNode = XmpNodeUtils.FindSchemaNode(xmp.Root, schemaNs, false); } else // !baseSchema && baseProperty { // No schema but property provided -> error throw new XmpException("Schema namespace URI is required", XmpError.BADSCHEMA); } // create iterator if (startNode != null) { _nodeIterator = (!_options.JustChildren) ? new NodeIterator(this, startNode, initialPath, 1) : new NodeIteratorChildren(this, startNode, initialPath); } else { // create null iterator _nodeIterator = EmptyList.GetEnumerator(); } }
/// <summary> /// Compose the path expression to select an alternate item by a field's value. The path syntax /// allows two forms of "content addressing" that may be used to select an item in an /// array of alternatives. The form used in ComposeFieldSelector lets you select an item in an /// array of structs based on the value of one of the fields in the structs. The other form of /// content addressing is shown in ComposeLangSelector. For example, consider a simple struct /// that has two fields, the name of a city and the URI of an FTP site in that city. Use this to /// create an array of download alternatives. You can show the user a popup built from the values /// of the city fields. You can then get the corresponding URI as follows: /// <p> /// <blockquote> /// /// <pre> /// String path = composeFieldSelector ( schemaNs, "Downloads", fieldNs, /// "City", chosenCity ); /// XMPProperty prop = xmpObj.getStructField ( schemaNs, path, fieldNs, "URI" ); /// </pre> /// /// </blockquote> /// </summary> /// <param name="arrayName"> The name of the array. May be a general path expression, must not be /// <code>null</code> or the empty string. </param> /// <param name="fieldNs"> The namespace URI for the field used as the selector. Must not be /// <code>null</code> or the empty string. </param> /// <param name="fieldName"> The name of the field used as the selector. Must be a simple XML name, must /// not be <code>null</code> or the empty string. It must be the name of a field that is /// itself simple. </param> /// <param name="fieldValue"> The desired value of the field. </param> /// <returns> Returns the composed path. This will be of the form /// <tt>ns:arrayName[fNS:fieldName='fieldValue']</tt>, where "ns" is the /// prefix for schemaNs and "fNS" is the prefix for fieldNs. </returns> /// <exception cref="XmpException"> Thrown if the path to create is not valid. </exception> public static string ComposeFieldSelector(string arrayName, string fieldNs, string fieldName, string fieldValue) { XmpPath fieldPath = XmpPathParser.ExpandXPath(fieldNs, fieldName); if (fieldPath.Size() != 2) { throw new XmpException("The fieldName name must be simple", XmpError.BADXPATH); } return(arrayName + '[' + fieldPath.GetSegment((int)XmpPath.STEP_ROOT_PROP).Name + "=\"" + fieldValue + "\"]"); }
/// <summary> /// Compose the path expression for a qualifier. /// </summary> /// <param name="qualNs"> The namespace URI for the qualifier. May be <code>null</code> or the empty /// string if the qualifier is in the XML empty namespace. </param> /// <param name="qualName"> The name of the qualifier. Must be a simple XML name, must not be /// <code>null</code> or the empty string. </param> /// <returns> Returns the composed path. This will be of the form /// <tt>ns:propName/?qNS:qualName</tt>, where "ns" is the prefix for /// schemaNs and "qNS" is the prefix for qualNs. </returns> /// <exception cref="XmpException"> Thrown if the path to create is not valid. </exception> public static string ComposeQualifierPath(string qualNs, string qualName) { AssertQualNs(qualNs); AssertQualName(qualName); XmpPath qualPath = XmpPathParser.ExpandXPath(qualNs, qualName); if (qualPath.Size() != 2) { throw new XmpException("The qualifier name must be simple", XmpError.BADXPATH); } return("/?" + qualPath.GetSegment((int)XmpPath.STEP_ROOT_PROP).Name); }
/// <summary> /// Compose the path expression for a field in a struct. The result can be added to the /// path of /// /// </summary> /// <param name="fieldNs"> The namespace URI for the field. Must not be <code>null</code> or the empty /// string. </param> /// <param name="fieldName"> The name of the field. Must be a simple XML name, must not be /// <code>null</code> or the empty string. </param> /// <returns> Returns the composed path. This will be of the form /// <tt>ns:structName/fNS:fieldName</tt>, where "ns" is the prefix for /// schemaNs and "fNS" is the prefix for fieldNs. </returns> /// <exception cref="XmpException"> Thrown if the path to create is not valid. </exception> public static string ComposeStructFieldPath(string fieldNs, string fieldName) { AssertFieldNs(fieldNs); AssertFieldName(fieldName); XmpPath fieldPath = XmpPathParser.ExpandXPath(fieldNs, fieldName); if (fieldPath.Size() != 2) { throw new XmpException("The field name must be simple", XmpError.BADXPATH); } return('/' + fieldPath.GetSegment((int)XmpPath.STEP_ROOT_PROP).Name); }
/// <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 <c>setProperty()</c>) /// </param> /// <param name="leafOptions"> /// the options for the created leaf nodes (only when /// <c>createNodes == true</c>). /// </param> /// <returns>Returns the node if found or created or <c>null</c>.</returns> /// <exception cref="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", XmpErrorCode.BadXPath); } // Root of implicitly created subtree to possible delete it later. // Valid only if leaf is new. XmpNode rootImplicitNode = null; // resolve schema step var currNode = FindSchemaNode(xmpTree, xpath.GetSegment(XmpPath.StepSchema).Name, createNodes); if (currNode == null) { return(null); } if (currNode.IsImplicit) { currNode.IsImplicit = 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 (var 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); } if (currNode.IsImplicit) { // clear the implicit node flag currNode.IsImplicit = 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).AliasForm != 0) { currNode.Options.SetOption(xpath.GetSegment(i).AliasForm, true); } else { // "CheckImplicitStruct" in C++ if (i < xpath.Size() - 1 && xpath.GetSegment(i).Kind == XmpPathStepType.StructFieldStep && !currNode.Options.IsCompositeProperty) { currNode.Options.IsStruct = true; } } if (rootImplicitNode == null) { rootImplicitNode = currNode; } } } } catch (XmpException) { // 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.Options.MergeWith(leafOptions); currNode.Options = currNode.Options; } return(currNode); }
/// <seealso cref= XMPUtils#removeProperties(XMPMeta, String, String, boolean, boolean) /// </seealso> /// <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="XmpException"> If metadata processing fails </exception> public static void RemoveProperties(IXmpMeta xmp, string schemaNs, string propName, bool doAllProperties, bool includeAliases) { ParameterAsserts.AssertImplementation(xmp); XmpMetaImpl xmpImpl = (XmpMetaImpl)xmp; if (!string.IsNullOrEmpty(propName)) { // 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 (string.IsNullOrEmpty(schemaNs)) { throw new XmpException("Property name requires schema namespace", XmpError.BADPARAM); } XmpPath expPath = XmpPathParser.ExpandXPath(schemaNs, propName); XmpNode propNode = XmpNodeUtils.FindNode(xmpImpl.Root, expPath, false, null); if (propNode != null) { if (doAllProperties || !Utils.IsInternalProperty(expPath.GetSegment((int)XmpPath.STEP_SCHEMA).Name, expPath.GetSegment((int)XmpPath.STEP_ROOT_PROP).Name)) { XmpNode parent = propNode.Parent; parent.RemoveChild(propNode); if (parent.Options.SchemaNode && !parent.HasChildren()) { // remove empty schema node parent.Parent.RemoveChild(parent); } } } } else if (!string.IsNullOrEmpty(schemaNs)) { // 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.Root, schemaNs, false); if (schemaNode != null) { if (RemoveSchemaChildren(schemaNode, doAllProperties)) { xmpImpl.Root.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. IXmpAliasInfo[] aliases = XmpMetaFactory.SchemaRegistry.FindAliases(schemaNs); for (int i = 0; i < aliases.Length; i++) { IXmpAliasInfo info = aliases[i]; XmpPath path = XmpPathParser.ExpandXPath(info.Namespace, info.PropName); XmpNode actualProp = XmpNodeUtils.FindNode(xmpImpl.Root, path, false, null); if (actualProp != null) { XmpNode parent = actualProp.Parent; 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. ArrayList schemasToRemove = new ArrayList(); for (IEnumerator it = xmpImpl.Root.IterateChildren(); it.MoveNext();) { XmpNode schema = (XmpNode)it.Current; if (schema == null) { continue; } if (RemoveSchemaChildren(schema, doAllProperties)) { schemasToRemove.Add(schema); } } foreach (XmpNode xmpNode in schemasToRemove) { xmpImpl.Root.Children.Remove(xmpNode); } schemasToRemove.Clear(); } }