1f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// =================================================================================================
2f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// ADOBE SYSTEMS INCORPORATED
3f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// Copyright 2006 Adobe Systems Incorporated
4f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// All Rights Reserved
5f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling//
6f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the terms
7f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// of the Adobe license agreement accompanying it.
8f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling// =================================================================================================
9f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
10f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingpackage com.adobe.xmp.impl;
11f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
12f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.util.GregorianCalendar;
13f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.util.Iterator;
14f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
15f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPConst;
16f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPDateTime;
17f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPDateTimeFactory;
18f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPError;
19f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPException;
20f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPMetaFactory;
21f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPUtils;
22f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.impl.xpath.XMPPath;
23f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.impl.xpath.XMPPathSegment;
24f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.options.AliasOptions;
25f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.options.PropertyOptions;
26f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
27f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
28f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling/**
29f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * Utilities for <code>XMPNode</code>.
30f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling *
31f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @since   Aug 28, 2006
32f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */
33f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingpublic class XMPNodeUtils implements XMPConst
34f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling{
35f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/** */
36f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static final int CLT_NO_VALUES = 0;
37f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/** */
38f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static final int CLT_SPECIFIC_MATCH = 1;
39f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/** */
40f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static final int CLT_SINGLE_GENERIC = 2;
41f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/** */
42f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static final int CLT_MULTIPLE_GENERIC = 3;
43f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/** */
44f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static final int CLT_XDEFAULT = 4;
45f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/** */
46f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static final int CLT_FIRST_ITEM = 5;
47f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
48f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
49f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
50f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Private Constructor
51f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
52f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private XMPNodeUtils()
53f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
54f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// EMPTY
55f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
56f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
57f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
58f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
59f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Find or create a schema node if <code>createNodes</code> is false and
60f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
61f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param tree the root of the xmp tree.
62f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param namespaceURI a namespace
63f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param createNodes a flag indicating if the node shall be created if not found.
64f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * 		  <em>Note:</em> The namespace must be registered prior to this call.
65f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
66f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the schema node if found, <code>null</code> otherwise.
67f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * 		   Note: If <code>createNodes</code> is <code>true</code>, it is <b>always</b>
68f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * 		   returned a valid node.
69f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException An exception is only thrown if an error occurred, not if a
70f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *         		node was not found.
71f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
72f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static XMPNode findSchemaNode(XMPNode tree, String namespaceURI,
73f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			boolean createNodes)
74f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throws XMPException
75f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
76f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return findSchemaNode(tree, namespaceURI, null, createNodes);
77f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
78f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
79f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
80f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
81f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Find or create a schema node if <code>createNodes</code> is true.
82f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
83f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param tree the root of the xmp tree.
84f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param namespaceURI a namespace
85f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param suggestedPrefix If a prefix is suggested, the namespace is allowed to be registered.
86f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param createNodes a flag indicating if the node shall be created if not found.
87f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * 		  <em>Note:</em> The namespace must be registered prior to this call.
88f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
89f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the schema node if found, <code>null</code> otherwise.
90f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * 		   Note: If <code>createNodes</code> is <code>true</code>, it is <b>always</b>
91f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * 		   returned a valid node.
92f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException An exception is only thrown if an error occurred, not if a
93f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *         		node was not found.
94f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
95f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static XMPNode findSchemaNode(XMPNode tree, String namespaceURI, String suggestedPrefix,
96f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			boolean createNodes)
97f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throws XMPException
98f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
99f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		assert tree.getParent() == null; // make sure that its the root
100f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		XMPNode schemaNode = tree.findChildByName(namespaceURI);
101f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
102f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (schemaNode == null  &&  createNodes)
103f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
104f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			schemaNode = new XMPNode(namespaceURI,
105f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				new PropertyOptions()
106f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					.setSchemaNode(true));
107f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			schemaNode.setImplicit(true);
108f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
109f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// only previously registered schema namespaces are allowed in the XMP tree.
110f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			String prefix = XMPMetaFactory.getSchemaRegistry().getNamespacePrefix(namespaceURI);
111f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (prefix == null)
112f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
113f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (suggestedPrefix != null  &&  suggestedPrefix.length() != 0)
114f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
115f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					prefix = XMPMetaFactory.getSchemaRegistry().registerNamespace(namespaceURI,
116f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							suggestedPrefix);
117f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
118f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				else
119f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
120f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					throw new XMPException("Unregistered schema namespace URI",
121f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							XMPError.BADSCHEMA);
122f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
123f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
124f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
125f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			schemaNode.setValue(prefix);
126f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
127f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			tree.addChild(schemaNode);
128f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
129f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
130f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return schemaNode;
131f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
132f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
133f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
134f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
135f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Find or create a child node under a given parent node. If the parent node is no
136f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Returns the found or created child node.
137f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
138f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param parent
139f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *            the parent node
140f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param childName
141f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *            the node name to find
142f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param createNodes
143f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *            flag, if new nodes shall be created.
144f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the found or created node or <code>null</code>.
145f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException Thrown if
146f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
147f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static XMPNode findChildNode(XMPNode parent, String childName, boolean createNodes)
148f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throws XMPException
149f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
150f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (!parent.getOptions().isSchemaNode() && !parent.getOptions().isStruct())
151f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
152f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (!parent.isImplicit())
153f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
154f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				throw new XMPException("Named children only allowed for schemas and structs",
155f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						XMPError.BADXPATH);
156f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
157f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (parent.getOptions().isArray())
158f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
159f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				throw new XMPException("Named children not allowed for arrays",
160f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						XMPError.BADXPATH);
161f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
162f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (createNodes)
163f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
164f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				parent.getOptions().setStruct(true);
165f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
166f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
167f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
168f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		XMPNode childNode = parent.findChildByName(childName);
169f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
170f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (childNode == null  &&  createNodes)
171f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
172f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			PropertyOptions options = new PropertyOptions();
173f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			childNode = new XMPNode(childName, options);
174f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			childNode.setImplicit(true);
175f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			parent.addChild(childNode);
176f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
177f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
178f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		assert childNode != null ||  !createNodes;
179f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
180f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return childNode;
181f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
182f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
183f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
184f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
185f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Follow an expanded path expression to find or create a node.
186f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
187f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param xmpTree the node to begin the search.
188f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param xpath the complete xpath
189f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param createNodes flag if nodes shall be created
190f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * 			(when called by <code>setProperty()</code>)
191f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param leafOptions the options for the created leaf nodes (only when
192f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *			<code>createNodes == true</code>).
193f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the node if found or created or <code>null</code>.
194f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException An exception is only thrown if an error occurred,
195f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * 			not if a node was not found.
196f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
197f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static XMPNode findNode(XMPNode xmpTree, XMPPath xpath, boolean createNodes,
198f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		PropertyOptions leafOptions) throws XMPException
199f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
200f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// check if xpath is set.
201f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (xpath == null  ||  xpath.size() == 0)
202f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
203f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throw new XMPException("Empty XMPPath", XMPError.BADXPATH);
204f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
205f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
206f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// Root of implicitly created subtree to possible delete it later.
207f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// Valid only if leaf is new.
208f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		XMPNode rootImplicitNode = null;
209f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		XMPNode currNode = null;
210f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
211f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// resolve schema step
212f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		currNode = findSchemaNode(xmpTree,
213f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			xpath.getSegment(XMPPath.STEP_SCHEMA).getName(), createNodes);
214f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (currNode == null)
215f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
216f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			return null;
217f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
218f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else if (currNode.isImplicit())
219f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
220f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			currNode.setImplicit(false);	// Clear the implicit node bit.
221f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			rootImplicitNode = currNode;	// Save the top most implicit node.
222f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
223f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
224f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
225f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// Now follow the remaining steps of the original XMPPath.
226f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		try
227f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
228f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			for (int i = 1; i < xpath.size(); i++)
229f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
230f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				currNode = followXPathStep(currNode, xpath.getSegment(i), createNodes);
231f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (currNode == null)
232f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
233f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					if (createNodes)
234f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					{
235f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						// delete implicitly created nodes
236f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						deleteNode(rootImplicitNode);
237f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					}
238f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					return null;
239f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
240f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				else if (currNode.isImplicit())
241f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
242f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					// clear the implicit node flag
243f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					currNode.setImplicit(false);
244f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
245f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					// if node is an ALIAS (can be only in root step, auto-create array
246f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					// when the path has been resolved from a not simple alias type
247f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					if (i == 1  &&
248f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						xpath.getSegment(i).isAlias()  &&
249f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						xpath.getSegment(i).getAliasForm() != 0)
250f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					{
251f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						currNode.getOptions().setOption(xpath.getSegment(i).getAliasForm(), true);
252f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					}
253f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					// "CheckImplicitStruct" in C++
254f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					else if (i < xpath.size() - 1  &&
255f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						xpath.getSegment(i).getKind() == XMPPath.STRUCT_FIELD_STEP  &&
256f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						!currNode.getOptions().isCompositeProperty())
257f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					{
258f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						currNode.getOptions().setStruct(true);
259f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					}
260f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
261f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					if (rootImplicitNode == null)
262f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					{
263f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						rootImplicitNode = currNode;	// Save the top most implicit node.
264f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					}
265f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
266f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
267f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
268f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		catch (XMPException e)
269f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
270f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// if new notes have been created prior to the error, delete them
271f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (rootImplicitNode != null)
272f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
273f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				deleteNode(rootImplicitNode);
274f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
275f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throw e;
276f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
277f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
278f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
279f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (rootImplicitNode != null)
280f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
281f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// set options only if a node has been successful created
282f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			currNode.getOptions().mergeWith(leafOptions);
283f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			currNode.setOptions(currNode.getOptions());
284f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
285f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
286f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return currNode;
287f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
288f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
289f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
290f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
291f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Deletes the the given node and its children from its parent.
292f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Takes care about adjusting the flags.
293f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param node the top-most node to delete.
294f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
295f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static void deleteNode(XMPNode node)
296f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
297f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		XMPNode parent = node.getParent();
298f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
299f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (node.getOptions().isQualifier())
300f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
301f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// root is qualifier
302f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			parent.removeQualifier(node);
303f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
304f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else
305f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
306f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// root is NO qualifier
307f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			parent.removeChild(node);
308f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
309f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
310f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// delete empty Schema nodes
311f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (!parent.hasChildren()  &&  parent.getOptions().isSchemaNode())
312f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
313f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			parent.getParent().removeChild(parent);
314f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
315f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
316f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
317f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
318f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
319f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * This is setting the value of a leaf node.
320f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
321f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param node an XMPNode
322f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param value a value
323f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
324f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static void setNodeValue(XMPNode node, Object value)
325f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
326f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		String strValue = serializeNodeValue(value);
327f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (!(node.getOptions().isQualifier()  &&  XML_LANG.equals(node.getName())))
328f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
329f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			node.setValue(strValue);
330f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
331f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else
332f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
333f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			node.setValue(Utils.normalizeLangValue(strValue));
334f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
335f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
336f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
337f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
338f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
339f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Verifies the PropertyOptions for consistancy and updates them as needed.
340f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * If options are <code>null</code> they are created with default values.
341f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
342f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param options the <code>PropertyOptions</code>
343f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param itemValue the node value to set
344f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the updated options.
345f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException If the options are not consistant.
346f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
347f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static PropertyOptions verifySetOptions(PropertyOptions options, Object itemValue)
348f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throws XMPException
349f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
350f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// create empty and fix existing options
351f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (options == null)
352f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
353f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// set default options
354f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			options = new PropertyOptions();
355f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
356f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
357f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (options.isArrayAltText())
358f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
359f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			options.setArrayAlternate(true);
360f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
361f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
362f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (options.isArrayAlternate())
363f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
364f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			options.setArrayOrdered(true);
365f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
366f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
367f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (options.isArrayOrdered())
368f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
369f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			options.setArray(true);
370f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
371f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
372f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (options.isCompositeProperty() && itemValue != null && itemValue.toString().length() > 0)
373f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
374f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throw new XMPException("Structs and arrays can't have values",
375f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPError.BADOPTIONS);
376f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
377f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
378f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		options.assertConsistency(options.getOptions());
379f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
380f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return options;
381f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
382f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
383f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
384f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
385f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Converts the node value to String, apply special conversions for defined
386f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * types in XMP.
387f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
388f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param value
389f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *            the node value to set
390f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the String representation of the node value.
391f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
392f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static String serializeNodeValue(Object value)
393f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
394f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		String strValue;
395f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (value == null)
396f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
397f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			strValue = null;
398f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
399f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else if (value instanceof Boolean)
400f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
401f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			strValue = XMPUtils.convertFromBoolean(((Boolean) value).booleanValue());
402f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
403f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else if (value instanceof Integer)
404f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
405f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			strValue = XMPUtils.convertFromInteger(((Integer) value).intValue());
406f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
407f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else if (value instanceof Long)
408f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
409f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			strValue = XMPUtils.convertFromLong(((Long) value).longValue());
410f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
411f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else if (value instanceof Double)
412f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
413f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			strValue = XMPUtils.convertFromDouble(((Double) value).doubleValue());
414f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
415f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else if (value instanceof XMPDateTime)
416f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
417f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			strValue = XMPUtils.convertFromDate((XMPDateTime) value);
418f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
419f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else if (value instanceof GregorianCalendar)
420f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
421f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPDateTime dt = XMPDateTimeFactory.createFromCalendar((GregorianCalendar) value);
422f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			strValue = XMPUtils.convertFromDate(dt);
423f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
424f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else if (value instanceof byte[])
425f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
426f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			strValue = XMPUtils.encodeBase64((byte[]) value);
427f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
428f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else
429f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
430f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			strValue = value.toString();
431f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
432f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
433f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return strValue != null ? Utils.removeControlChars(strValue) : null;
434f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
435f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
436f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
437f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
438f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * After processing by ExpandXPath, a step can be of these forms:
439f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <ul>
440f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * 	<li>qualName - A top level property or struct field.
441f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>[index] - An element of an array.
442f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>[last()] - The last element of an array.
443f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>[qualName="value"] - An element in an array of structs, chosen by a field value.
444f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>[?qualName="value"] - An element in an array, chosen by a qualifier value.
445f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>?qualName - A general qualifier.
446f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * </ul>
447f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Find the appropriate child node, resolving aliases, and optionally creating nodes.
448f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
449f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param parentNode the node to start to start from
450f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param nextStep the xpath segment
451f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param createNodes
452f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return returns the found or created XMPPath node
453f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException
454f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
455f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static XMPNode followXPathStep(
456f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPNode parentNode,
457f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPPathSegment nextStep,
458f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				boolean createNodes) throws XMPException
459f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
460f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		XMPNode nextNode = null;
461f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		int index = 0;
462f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		int stepKind = nextStep.getKind();
463f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
464f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (stepKind == XMPPath.STRUCT_FIELD_STEP)
465f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
466f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			nextNode = findChildNode(parentNode, nextStep.getName(), createNodes);
467f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
468f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else if (stepKind == XMPPath.QUALIFIER_STEP)
469f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
470f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			nextNode = findQualifierNode(
471f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				parentNode, nextStep.getName().substring(1), createNodes);
472f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
473f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else
474f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
475f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// This is an array indexing step. First get the index, then get the node.
476f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
477f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (!parentNode.getOptions().isArray())
478f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
479f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				throw new XMPException("Indexing applied to non-array", XMPError.BADXPATH);
480f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
481f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
482f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (stepKind == XMPPath.ARRAY_INDEX_STEP)
483f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
484f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				index = findIndexedItem(parentNode, nextStep.getName(), createNodes);
485f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
486f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (stepKind == XMPPath.ARRAY_LAST_STEP)
487f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
488f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				index = parentNode.getChildrenLength();
489f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
490f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (stepKind == XMPPath.FIELD_SELECTOR_STEP)
491f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
492f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				String[] result = Utils.splitNameAndValue(nextStep.getName());
493f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				String fieldName = result[0];
494f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				String fieldValue = result[1];
495f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				index = lookupFieldSelector(parentNode, fieldName, fieldValue);
496f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
497f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (stepKind == XMPPath.QUAL_SELECTOR_STEP)
498f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
499f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				String[] result = Utils.splitNameAndValue(nextStep.getName());
500f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				String qualName = result[0];
501f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				String qualValue = result[1];
502f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				index = lookupQualSelector(
503f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					parentNode, qualName, qualValue, nextStep.getAliasForm());
504f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
505f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else
506f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
507f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				throw new XMPException("Unknown array indexing step in FollowXPathStep",
508f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						XMPError.INTERNALFAILURE);
509f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
510f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
511f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (1 <= index  &&  index <=  parentNode.getChildrenLength())
512f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
513f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				nextNode = parentNode.getChild(index);
514f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
515f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
516f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
517f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return nextNode;
518f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
519f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
520f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
521f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
522f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Find or create a qualifier node under a given parent node. Returns a pointer to the
523f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * qualifier node, and optionally an iterator for the node's position in
524f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * the parent's vector of qualifiers. The iterator is unchanged if no qualifier node (null)
525f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * is returned.
526f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <em>Note:</em> On entry, the qualName parameter must not have the leading '?' from the
527f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * XMPPath step.
528f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
529f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param parent the parent XMPNode
530f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param qualName the qualifier name
531f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param createNodes flag if nodes shall be created
532f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the qualifier node if found or created, <code>null</code> otherwise.
533f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException
534f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
535f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static XMPNode findQualifierNode(XMPNode parent, String qualName, boolean createNodes)
536f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throws XMPException
537f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
538f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		assert !qualName.startsWith("?");
539f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
540f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		XMPNode qualNode = parent.findQualifierByName(qualName);
541f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
542f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (qualNode == null  &&  createNodes)
543f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
544f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			qualNode = new XMPNode(qualName, null);
545f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			qualNode.setImplicit(true);
546f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
547f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			parent.addQualifier(qualNode);
548f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
549f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
550f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return qualNode;
551f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
552f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
553f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
554f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
555f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param arrayNode an array node
556f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param segment the segment containing the array index
557f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param createNodes flag if new nodes are allowed to be created.
558f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the index or index = -1 if not found
559f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException Throws Exceptions
560f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
561f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static int findIndexedItem(XMPNode arrayNode, String segment, boolean createNodes)
562f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throws XMPException
563f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
564f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		int index = 0;
565f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
566f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		try
567f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
568f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			segment = segment.substring(1, segment.length() - 1);
569f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			index = Integer.parseInt(segment);
570f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (index < 1)
571f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
572f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				throw new XMPException("Array index must be larger than zero",
573f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						XMPError.BADXPATH);
574f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
575f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
576f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		catch (NumberFormatException e)
577f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
578f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throw new XMPException("Array index not digits.", XMPError.BADXPATH);
579f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
580f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
581f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (createNodes  &&  index == arrayNode.getChildrenLength() + 1)
582f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
583f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// Append a new last + 1 node.
584f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode newItem = new XMPNode(ARRAY_ITEM_NAME, null);
585f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			newItem.setImplicit(true);
586f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			arrayNode.addChild(newItem);
587f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
588f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
589f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return index;
590f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
591f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
592f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
593f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
594f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Searches for a field selector in a node:
595f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * [fieldName="value] - an element in an array of structs, chosen by a field value.
596f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * No implicit nodes are created by field selectors.
597f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
598f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param arrayNode
599f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param fieldName
600f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param fieldValue
601f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the index of the field if found, otherwise -1.
602f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException
603f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
604f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static int lookupFieldSelector(XMPNode arrayNode, String fieldName, String fieldValue)
605f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		throws XMPException
606f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
607f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		int result = -1;
608f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
609f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		for (int index = 1; index <= arrayNode.getChildrenLength()  &&  result < 0; index++)
610f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
611f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode currItem = arrayNode.getChild(index);
612f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
613f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (!currItem.getOptions().isStruct())
614f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
615f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				throw new XMPException("Field selector must be used on array of struct",
616f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						XMPError.BADXPATH);
617f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
618f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
619f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			for (int f = 1; f <= currItem.getChildrenLength(); f++)
620f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
621f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPNode currField = currItem.getChild(f);
622f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (!fieldName.equals(currField.getName()))
623f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
624f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					continue;
625f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
626f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (fieldValue.equals(currField.getValue()))
627f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
628f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					result = index;
629f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					break;
630f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
631f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
632f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
633f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
634f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return result;
635f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
636f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
637f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
638f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
639f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Searches for a qualifier selector in a node:
640f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * [?qualName="value"] - an element in an array, chosen by a qualifier value.
641f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * No implicit nodes are created for qualifier selectors,
642f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * except for an alias to an x-default item.
643f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
644f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param arrayNode an array node
645f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param qualName the qualifier name
646f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param qualValue the qualifier value
647f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param aliasForm in case the qual selector results from an alias,
648f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * 		  an x-default node is created if there has not been one.
649f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the index of th
650f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException
651f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
652f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static int lookupQualSelector(XMPNode arrayNode, String qualName,
653f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		String qualValue, int aliasForm) throws XMPException
654f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
655f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (XML_LANG.equals(qualName))
656f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
657f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			qualValue = Utils.normalizeLangValue(qualValue);
658f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			int index = XMPNodeUtils.lookupLanguageItem(arrayNode, qualValue);
659f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (index < 0  &&  (aliasForm & AliasOptions.PROP_ARRAY_ALT_TEXT) > 0)
660f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
661f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPNode langNode = new XMPNode(ARRAY_ITEM_NAME, null);
662f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPNode xdefault = new XMPNode(XML_LANG, X_DEFAULT, null);
663f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				langNode.addQualifier(xdefault);
664f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				arrayNode.addChild(1, langNode);
665f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				return 1;
666f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
667f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else
668f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
669f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				return index;
670f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
671f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
672f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else
673f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
674f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			for (int index = 1; index < arrayNode.getChildrenLength(); index++)
675f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
676f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPNode currItem = arrayNode.getChild(index);
677f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
678f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				for (Iterator it = currItem.iterateQualifier(); it.hasNext();)
679f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
680f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					XMPNode qualifier = (XMPNode) it.next();
681f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					if (qualName.equals(qualifier.getName())  &&
682f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						qualValue.equals(qualifier.getValue()))
683f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					{
684f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						return index;
685f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					}
686f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
687f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
688f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			return -1;
689f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
690f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
691f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
692f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
693f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
694f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Make sure the x-default item is first. Touch up &quot;single value&quot;
695f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * arrays that have a default plus one real language. This case should have
696f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * the same value for both items. Older Adobe apps were hardwired to only
697f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * use the &quot;x-default&quot; item, so we copy that value to the other
698f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * item.
699f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
700f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param arrayNode
701f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *            an alt text array node
702f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
703f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static void normalizeLangArray(XMPNode arrayNode)
704f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
705f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (!arrayNode.getOptions().isArrayAltText())
706f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
707f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			return;
708f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
709f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
710f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// check if node with x-default qual is first place
711f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		for (int i = 2; i <= arrayNode.getChildrenLength(); i++)
712f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
713f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode child = arrayNode.getChild(i);
714f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (child.hasQualifier() && X_DEFAULT.equals(child.getQualifier(1).getValue()))
715f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
716f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				// move node to first place
717f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				try
718f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
719f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					arrayNode.removeChild(i);
720f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					arrayNode.addChild(1, child);
721f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
722f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				catch (XMPException e)
723f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
724f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					// cannot occur, because same child is removed before
725f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					assert false;
726f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
727f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
728f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (i == 2)
729f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
730f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					arrayNode.getChild(2).setValue(child.getValue());
731f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
732f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				break;
733f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
734f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
735f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
736f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
737f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
738f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
739f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * See if an array is an alt-text array. If so, make sure the x-default item
740f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * is first.
741f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
742f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param arrayNode
743f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *            the array node to check if its an alt-text array
744f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
745f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static void detectAltText(XMPNode arrayNode)
746f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
747f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (arrayNode.getOptions().isArrayAlternate() && arrayNode.hasChildren())
748f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
749f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			boolean isAltText = false;
750f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			for (Iterator it = arrayNode.iterateChildren(); it.hasNext();)
751f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
752f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPNode child = (XMPNode) it.next();
753f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (child.getOptions().getHasLanguage())
754f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
755f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					isAltText = true;
756f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					break;
757f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
758f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
759f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
760f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (isAltText)
761f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
762f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				arrayNode.getOptions().setArrayAltText(true);
763f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				normalizeLangArray(arrayNode);
764f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
765f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
766f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
767f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
768f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
769f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
770f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Appends a language item to an alt text array.
771f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
772f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param arrayNode the language array
773f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param itemLang the language of the item
774f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param itemValue the content of the item
775f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException Thrown if a duplicate property is added
776f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
777f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static void appendLangItem(XMPNode arrayNode, String itemLang, String itemValue)
778f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throws XMPException
779f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
780f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		XMPNode newItem = new XMPNode(ARRAY_ITEM_NAME, itemValue, null);
781f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		XMPNode langQual = new XMPNode(XML_LANG, itemLang, null);
782f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		newItem.addQualifier(langQual);
783f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
784f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (!X_DEFAULT.equals(langQual.getValue()))
785f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
786f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			arrayNode.addChild(newItem);
787f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
788f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else
789f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
790f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			arrayNode.addChild(1, newItem);
791f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
792f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
793f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
794f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
795f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
796f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <ol>
797f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>Look for an exact match with the specific language.
798f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>If a generic language is given, look for partial matches.
799f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>Look for an "x-default"-item.
800f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <li>Choose the first item.
801f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * </ol>
802f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
803f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param arrayNode
804f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *            the alt text array node
805f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param genericLang
806f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *            the generic language
807f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param specificLang
808f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *            the specific language
809f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the kind of match as an Integer and the found node in an
810f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *         array.
811f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
812f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException
813f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
814f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static Object[] chooseLocalizedText(XMPNode arrayNode, String genericLang, String specificLang)
815f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throws XMPException
816f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
817f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// See if the array has the right form. Allow empty alt arrays,
818f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// that is what parsing returns.
819f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (!arrayNode.getOptions().isArrayAltText())
820f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
821f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throw new XMPException("Localized text array is not alt-text", XMPError.BADXPATH);
822f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
823f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else if (!arrayNode.hasChildren())
824f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
825f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			return new Object[] { new Integer(XMPNodeUtils.CLT_NO_VALUES), null };
826f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
827f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
828f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		int foundGenericMatches = 0;
829f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		XMPNode resultNode = null;
830f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		XMPNode xDefault = null;
831f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
832f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// Look for the first partial match with the generic language.
833f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		for (Iterator it = arrayNode.iterateChildren(); it.hasNext();)
834f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
835f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode currItem = (XMPNode) it.next();
836f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
837f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// perform some checks on the current item
838f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (currItem.getOptions().isCompositeProperty())
839f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
840f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				throw new XMPException("Alt-text array item is not simple", XMPError.BADXPATH);
841f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
842f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (!currItem.hasQualifier()
843f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					|| !XML_LANG.equals(currItem.getQualifier(1).getName()))
844f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
845f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				throw new XMPException("Alt-text array item has no language qualifier",
846f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						XMPError.BADXPATH);
847f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
848f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
849f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			String currLang = currItem.getQualifier(1).getValue();
850f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
851f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// Look for an exact match with the specific language.
852f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (specificLang.equals(currLang))
853f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
854f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				return new Object[] { new Integer(XMPNodeUtils.CLT_SPECIFIC_MATCH), currItem };
855f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
856f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (genericLang != null && currLang.startsWith(genericLang))
857f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
858f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (resultNode == null)
859f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
860f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					resultNode = currItem;
861f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
862f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				// ! Don't return/break, need to look for other matches.
863f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				foundGenericMatches++;
864f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
865f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (X_DEFAULT.equals(currLang))
866f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
867f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				xDefault = currItem;
868f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
869f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
870f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
871f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// evaluate loop
872f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (foundGenericMatches == 1)
873f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
874f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			return new Object[] { new Integer(XMPNodeUtils.CLT_SINGLE_GENERIC), resultNode };
875f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
876f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else if (foundGenericMatches > 1)
877f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
878f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			return new Object[] { new Integer(XMPNodeUtils.CLT_MULTIPLE_GENERIC), resultNode };
879f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
880f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else if (xDefault != null)
881f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
882f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			return new Object[] { new Integer(XMPNodeUtils.CLT_XDEFAULT), xDefault };
883f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
884f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		else
885f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
886f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// Everything failed, choose the first item.
887f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			return new Object[] { new Integer(XMPNodeUtils.CLT_FIRST_ITEM), arrayNode.getChild(1) };
888f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
889f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
890f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
891f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
892f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
893f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Looks for the appropriate language item in a text alternative array.item
894f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
895f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param arrayNode
896f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *            an array node
897f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param language
898f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *            the requested language
899f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the index if the language has been found, -1 otherwise.
900f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException
901f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
902f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static int lookupLanguageItem(XMPNode arrayNode, String language) throws XMPException
903f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
904f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (!arrayNode.getOptions().isArray())
905f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
906f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throw new XMPException("Language item must be used on array", XMPError.BADXPATH);
907f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
908f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
909f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		for (int index = 1; index <= arrayNode.getChildrenLength(); index++)
910f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
911f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode child = arrayNode.getChild(index);
912f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (!child.hasQualifier() || !XML_LANG.equals(child.getQualifier(1).getName()))
913f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
914f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				continue;
915f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
916f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (language.equals(child.getQualifier(1).getValue()))
917f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
918f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				return index;
919f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
920f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
921f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
922f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return -1;
923f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
924f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling}
925