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.Calendar;
13f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.util.HashMap;
14f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.util.Iterator;
15f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport java.util.Map;
16f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
17f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPConst;
18f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPDateTime;
19f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPError;
20f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPException;
21f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPMeta;
22f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPMetaFactory;
23f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.XMPUtils;
24f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.impl.xpath.XMPPath;
25f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.impl.xpath.XMPPathParser;
26f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.options.ParseOptions;
27f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.options.PropertyOptions;
28f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingimport com.adobe.xmp.properties.XMPAliasInfo;
29f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
30f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling/**
31f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling * @since   Aug 18, 2006
32f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling */
33f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberlingpublic class XMPNormalizer
34f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling{
35f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/** caches the correct dc-property array forms */
36f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static Map dcArrayForms;
37f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/** init char tables */
38f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static
39f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
40f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		initDCArrays();
41f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
42f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
43f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
44f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
45f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Hidden constructor
46f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
47f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private XMPNormalizer()
48f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
49f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// EMPTY
50f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
51f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
52f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
53f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
54f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Normalizes a raw parsed XMPMeta-Object
55f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param xmp the raw metadata object
56f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param options the parsing options
57f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @return Returns the normalized metadata object
58f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException Collects all severe processing errors.
59f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
60f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	static XMPMeta process(XMPMetaImpl xmp, ParseOptions options) throws XMPException
61f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
62f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		XMPNode tree = xmp.getRoot();
63f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
64f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		touchUpDataModel(xmp);
65f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		moveExplicitAliases(tree, options);
66f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
67f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		tweakOldXMP(tree);
68f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
69f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		deleteEmptySchemas(tree);
70f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
71f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		return xmp;
72f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
73f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
74f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
75f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
76f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Tweak old XMP: Move an instance ID from rdf:about to the
77f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <em>xmpMM:InstanceID</em> property. An old instance ID usually looks
78f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * like &quot;uuid:bac965c4-9d87-11d9-9a30-000d936b79c4&quot;, plus InDesign
79f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * 3.0 wrote them like &quot;bac965c4-9d87-11d9-9a30-000d936b79c4&quot;. If
80f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * the name looks like a UUID simply move it to <em>xmpMM:InstanceID</em>,
81f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * don't worry about any existing <em>xmpMM:InstanceID</em>. Both will
82f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * only be present when a newer file with the <em>xmpMM:InstanceID</em>
83f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * property is updated by an old app that uses <em>rdf:about</em>.
84f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
85f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param tree the root of the metadata tree
86f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException Thrown if tweaking fails.
87f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
88f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static void tweakOldXMP(XMPNode tree) throws XMPException
89f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
90f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (tree.getName() != null  &&  tree.getName().length() >= Utils.UUID_LENGTH)
91f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
92f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			String nameStr = tree.getName().toLowerCase();
93f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (nameStr.startsWith("uuid:"))
94f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
95f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				nameStr = nameStr.substring(5);
96f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
97f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
98f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (Utils.checkUUIDFormat(nameStr))
99f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
100f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				// move UUID to xmpMM:InstanceID and remove it from the root node
101f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPPath path = XMPPathParser.expandXPath(XMPConst.NS_XMP_MM, "InstanceID");
102f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPNode idNode = XMPNodeUtils.findNode (tree, path, true, null);
103f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (idNode != null)
104f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
105f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					idNode.setOptions(null);	// Clobber any existing xmpMM:InstanceID.
106f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					idNode.setValue("uuid:" + nameStr);
107f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					idNode.removeChildren();
108f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					idNode.removeQualifiers();
109f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					tree.setName(null);
110f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
111f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				else
112f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
113f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					throw new XMPException("Failure creating xmpMM:InstanceID",
114f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							XMPError.INTERNALFAILURE);
115f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
116f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
117f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
118f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
119f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
120f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
121f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
122f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Visit all schemas to do general fixes and handle special cases.
123f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
124f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param xmp the metadata object implementation
125f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException Thrown if the normalisation fails.
126f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
127f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static void touchUpDataModel(XMPMetaImpl xmp) throws XMPException
128f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
129f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// make sure the DC schema is existing, because it might be needed within the normalization
130f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// if not touched it will be removed by removeEmptySchemas
131f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		XMPNodeUtils.findSchemaNode(xmp.getRoot(), XMPConst.NS_DC, true);
132f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
133f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// Do the special case fixes within each schema.
134f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		for (Iterator it = xmp.getRoot().iterateChildren(); it.hasNext();)
135f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
136f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode currSchema = (XMPNode) it.next();
137f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (XMPConst.NS_DC.equals(currSchema.getName()))
138f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
139f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				normalizeDCArrays(currSchema);
140f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
141f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (XMPConst.NS_EXIF.equals(currSchema.getName()))
142f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
143f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				// Do a special case fix for exif:GPSTimeStamp.
144f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				fixGPSTimeStamp(currSchema);
145f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPNode arrayNode = XMPNodeUtils.findChildNode(currSchema, "exif:UserComment",
146f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						false);
147f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (arrayNode != null)
148f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
149f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					repairAltText(arrayNode);
150f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
151f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
152f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (XMPConst.NS_DM.equals(currSchema.getName()))
153f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
154f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				// Do a special case migration of xmpDM:copyright to
155f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				// dc:rights['x-default'].
156f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPNode dmCopyright = XMPNodeUtils.findChildNode(currSchema, "xmpDM:copyright",
157f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						false);
158f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (dmCopyright != null)
159f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
160f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					migrateAudioCopyright(xmp, dmCopyright);
161f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
162f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
163f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (XMPConst.NS_XMP_RIGHTS.equals(currSchema.getName()))
164f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
165f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPNode arrayNode = XMPNodeUtils.findChildNode(currSchema, "xmpRights:UsageTerms",
166f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						false);
167f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (arrayNode != null)
168f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
169f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					repairAltText(arrayNode);
170f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
171f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
172f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
173f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
174f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
175f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
176f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
177f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Undo the denormalization performed by the XMP used in Acrobat 5.<br>
178f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * If a Dublin Core array had only one item, it was serialized as a simple
179f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * property. <br>
180f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * The <code>xml:lang</code> attribute was dropped from an
181f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <code>alt-text</code> item if the language was <code>x-default</code>.
182f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
183f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param dcSchema the DC schema node
184f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException Thrown if normalization fails
185f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
186f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static void normalizeDCArrays(XMPNode dcSchema) throws XMPException
187f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
188f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		for (int i = 1; i <= dcSchema.getChildrenLength(); i++)
189f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
190f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode currProp = dcSchema.getChild(i);
191f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
192f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			PropertyOptions arrayForm = (PropertyOptions) dcArrayForms.get(currProp.getName());
193f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (arrayForm == null)
194f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
195f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				continue;
196f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
197f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (currProp.getOptions().isSimple())
198f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
199f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				// create a new array and add the current property as child,
200f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				// if it was formerly simple
201f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPNode newArray = new XMPNode(currProp.getName(), arrayForm);
202f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				currProp.setName(XMPConst.ARRAY_ITEM_NAME);
203f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				newArray.addChild(currProp);
204f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				dcSchema.replaceChild(i, newArray);
205f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
206f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				// fix language alternatives
207f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (arrayForm.isArrayAltText()  &&  !currProp.getOptions().getHasLanguage())
208f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
209f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					XMPNode newLang = new XMPNode(XMPConst.XML_LANG, XMPConst.X_DEFAULT, null);
210f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					currProp.addQualifier(newLang);
211f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
212f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
213f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else
214f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
215f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				// clear array options and add corrected array form if it has been an array before
216f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				currProp.getOptions().setOption(
217f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					PropertyOptions.ARRAY  |
218f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					PropertyOptions.ARRAY_ORDERED  |
219f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					PropertyOptions.ARRAY_ALTERNATE  |
220f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					PropertyOptions.ARRAY_ALT_TEXT,
221f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					false);
222f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				currProp.getOptions().mergeWith(arrayForm);
223f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
224f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (arrayForm.isArrayAltText())
225f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
226f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					// applying for "dc:description", "dc:rights", "dc:title"
227f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					repairAltText(currProp);
228f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
229f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
230f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
231f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
232f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
233f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
234f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
235f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
236f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Make sure that the array is well-formed AltText. Each item must be simple
237f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * and have an "xml:lang" qualifier. If repairs are needed, keep simple
238f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * non-empty items by adding the "xml:lang" with value "x-repair".
239f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param arrayNode the property node of the array to repair.
240f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException Forwards unexpected exceptions.
241f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
242f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static void repairAltText(XMPNode arrayNode) throws XMPException
243f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
244f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (arrayNode == null  ||
245f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			!arrayNode.getOptions().isArray())
246f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
247f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// Already OK or not even an array.
248f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			return;
249f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
250f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
251f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// fix options
252f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		arrayNode.getOptions().setArrayOrdered(true).setArrayAlternate(true).setArrayAltText(true);
253f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
254f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		for (Iterator it = arrayNode.iterateChildren(); it.hasNext();)
255f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
256f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode currChild = (XMPNode) it.next();
257f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (currChild.getOptions().isCompositeProperty())
258f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
259f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				// Delete non-simple children.
260f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				it.remove();
261f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
262f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else if (!currChild.getOptions().getHasLanguage())
263f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
264f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				String childValue = currChild.getValue();
265f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (childValue == null  ||  childValue.length() == 0)
266f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
267f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					// Delete empty valued children that have no xml:lang.
268f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					it.remove();
269f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
270f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				else
271f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
272f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					// Add an xml:lang qualifier with the value "x-repair".
273f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					XMPNode repairLang = new XMPNode(XMPConst.XML_LANG, "x-repair", null);
274f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					currChild.addQualifier(repairLang);
275f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
276f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
277f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
278f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
279f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
280f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
281f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
282f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Visit all of the top level nodes looking for aliases. If there is
283f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * no base, transplant the alias subtree. If there is a base and strict
284f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * aliasing is on, make sure the alias and base subtrees match.
285f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
286f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param tree the root of the metadata tree
287f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param options th parsing options
288f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException Forwards XMP errors
289f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
290f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static void moveExplicitAliases(XMPNode tree, ParseOptions options)
291f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throws XMPException
292f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
293f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (!tree.getHasAliases())
294f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
295f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			return;
296f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
297f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		tree.setHasAliases(false);
298f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
299f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		boolean strictAliasing = options.getStrictAliasing();
300f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
301f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		for (Iterator schemaIt = tree.getUnmodifiableChildren().iterator(); schemaIt.hasNext();)
302f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
303f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode currSchema = (XMPNode) schemaIt.next();
304f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (!currSchema.getHasAliases())
305f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
306f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				continue;
307f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
308f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
309f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			for (Iterator propertyIt = currSchema.iterateChildren(); propertyIt.hasNext();)
310f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
311f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPNode currProp = (XMPNode) propertyIt.next();
312f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
313f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (!currProp.isAlias())
314f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
315f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					continue;
316f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
317f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
318f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				currProp.setAlias(false);
319f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
320f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				// Find the base path, look for the base schema and root node.
321f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPAliasInfo info = XMPMetaFactory.getSchemaRegistry()
322f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						.findAlias(currProp.getName());
323f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (info != null)
324f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
325f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					// find or create schema
326f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					XMPNode baseSchema = XMPNodeUtils.findSchemaNode(tree, info
327f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							.getNamespace(), null, true);
328f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					baseSchema.setImplicit(false);
329f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
330f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					XMPNode baseNode = XMPNodeUtils
331f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							.findChildNode(baseSchema,
332f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling								info.getPrefix() + info.getPropName(), false);
333f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					if (baseNode == null)
334f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					{
335f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						if (info.getAliasForm().isSimple())
336f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						{
337f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							// A top-to-top alias, transplant the property.
338f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							// change the alias property name to the base name
339f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							String qname = info.getPrefix() + info.getPropName();
340f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							currProp.setName(qname);
341f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							baseSchema.addChild(currProp);
342f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							// remove the alias property
343f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							propertyIt.remove();
344f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						}
345f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						else
346f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						{
347f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							// An alias to an array item,
348f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							// create the array and transplant the property.
349f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							baseNode = new XMPNode(info.getPrefix() + info.getPropName(), info
350f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling									.getAliasForm().toPropertyOptions());
351f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							baseSchema.addChild(baseNode);
352f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							transplantArrayItemAlias (propertyIt, currProp, baseNode);
353f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						}
354f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
355f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					}
356f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					else if (info.getAliasForm().isSimple())
357f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					{
358f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						// The base node does exist and this is a top-to-top alias.
359f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						// Check for conflicts if strict aliasing is on.
360f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						// Remove and delete the alias subtree.
361f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						if (strictAliasing)
362f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						{
363f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							compareAliasedSubtrees (currProp, baseNode, true);
364f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						}
365f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
366f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						propertyIt.remove();
367f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					}
368f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					else
369f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					{
370f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						// This is an alias to an array item and the array exists.
371f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						// Look for the aliased item.
372f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						// Then transplant or check & delete as appropriate.
373f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
374f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						XMPNode  itemNode = null;
375f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						if (info.getAliasForm().isArrayAltText())
376f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						{
377f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							int xdIndex = XMPNodeUtils.lookupLanguageItem(baseNode,
378f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling									XMPConst.X_DEFAULT);
379f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							if (xdIndex != -1)
380f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							{
381f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling								itemNode = baseNode.getChild(xdIndex);
382f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							}
383f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						}
384f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						else if (baseNode.hasChildren())
385f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						{
386f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							itemNode = baseNode.getChild(1);
387f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						}
388f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
389f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						if (itemNode == null)
390f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						{
391f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							transplantArrayItemAlias (propertyIt, currProp, baseNode);
392f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						}
393f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						else
394f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						{
395f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							if (strictAliasing)
396f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							{
397f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling								compareAliasedSubtrees (currProp, itemNode, true);
398f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							}
399f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
400f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling							propertyIt.remove();
401f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						}
402f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					}
403f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
404f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
405f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			currSchema.setHasAliases(false);
406f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
407f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
408f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
409f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
410f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
411f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Moves an alias node of array form to another schema into an array
412f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param propertyIt the property iterator of the old schema (used to delete the property)
413f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param childNode the node to be moved
414f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param baseArray the base array for the array item
415f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException Forwards XMP errors
416f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
417f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static void transplantArrayItemAlias(Iterator propertyIt, XMPNode childNode,
418f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode baseArray) throws XMPException
419f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
420f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (baseArray.getOptions().isArrayAltText())
421f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
422f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (childNode.getOptions().getHasLanguage())
423f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
424f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				throw new XMPException("Alias to x-default already has a language qualifier",
425f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						XMPError.BADXMP);
426f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
427f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
428f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode langQual = new XMPNode(XMPConst.XML_LANG, XMPConst.X_DEFAULT, null);
429f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			childNode.addQualifier(langQual);
430f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
431f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
432f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		propertyIt.remove();
433f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		childNode.setName(XMPConst.ARRAY_ITEM_NAME);
434f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		baseArray.addChild(childNode);
435f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
436f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
437f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
438f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
439f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Fixes the GPS Timestamp in EXIF.
440f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param exifSchema the EXIF schema node
441f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException Thrown if the date conversion fails.
442f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
443f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static void fixGPSTimeStamp(XMPNode exifSchema)
444f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throws XMPException
445f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
446f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// Note: if dates are not found the convert-methods throws an exceptions,
447f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// 		 and this methods returns.
448f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		XMPNode gpsDateTime = XMPNodeUtils.findChildNode(exifSchema, "exif:GPSTimeStamp", false);
449f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (gpsDateTime == null)
450f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
451f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			return;
452f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
453f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
454f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		try
455f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
456f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPDateTime binGPSStamp;
457f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPDateTime binOtherDate;
458f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
459f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			binGPSStamp = XMPUtils.convertToDate(gpsDateTime.getValue());
460f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (binGPSStamp.getYear() != 0  ||
461f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				binGPSStamp.getMonth() != 0  ||
462f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				binGPSStamp.getDay() != 0)
463f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
464f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				return;
465f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
466f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
467f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode otherDate = XMPNodeUtils.findChildNode(exifSchema, "exif:DateTimeOriginal",
468f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					false);
469f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (otherDate == null)
470f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
471f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				otherDate = XMPNodeUtils.findChildNode(exifSchema, "exif:DateTimeDigitized", false);
472f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
473f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
474f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			binOtherDate = XMPUtils.convertToDate(otherDate.getValue());
475f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			Calendar cal = binGPSStamp.getCalendar();
476f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			cal.set(Calendar.YEAR, binOtherDate.getYear());
477f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			cal.set(Calendar.MONTH, binOtherDate.getMonth());
478f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			cal.set(Calendar.DAY_OF_MONTH, binOtherDate.getDay());
479f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			binGPSStamp = new XMPDateTimeImpl(cal);
480f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			gpsDateTime.setValue(XMPUtils.convertFromDate (binGPSStamp));
481f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
482f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		catch (XMPException e)
483f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
484f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// Don't let a missing or bad date stop other things.
485f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			return;
486f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
487f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
488f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
489f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
490f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
491f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
492f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Remove all empty schemas from the metadata tree that were generated during the rdf parsing.
493f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param tree the root of the metadata tree
494f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
495f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static void deleteEmptySchemas(XMPNode tree)
496f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
497f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// Delete empty schema nodes. Do this last, other cleanup can make empty
498f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// schema.
499f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
500f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		for (Iterator it = tree.iterateChildren(); it.hasNext();)
501f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
502f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode schema = (XMPNode) it.next();
503f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (!schema.hasChildren())
504f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
505f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				it.remove();
506f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
507f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
508f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
509f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
510f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
511f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
512f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * The outermost call is special. The names almost certainly differ. The
513f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * qualifiers (and hence options) will differ for an alias to the x-default
514f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * item of a langAlt array.
515f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
516f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param aliasNode the alias node
517f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param baseNode the base node of the alias
518f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param outerCall marks the outer call of the recursion
519f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @throws XMPException Forwards XMP errors
520f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
521f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static void compareAliasedSubtrees(XMPNode aliasNode, XMPNode baseNode,
522f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			boolean outerCall) throws XMPException
523f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
524f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (!aliasNode.getValue().equals(baseNode.getValue())  ||
525f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			aliasNode.getChildrenLength() != baseNode.getChildrenLength())
526f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
527f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throw new XMPException("Mismatch between alias and base nodes", XMPError.BADXMP);
528f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
529f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
530f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		if (
531f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				!outerCall  &&
532f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				(!aliasNode.getName().equals(baseNode.getName())  ||
533f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				 !aliasNode.getOptions().equals(baseNode.getOptions())  ||
534f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				 aliasNode.getQualifierLength() != baseNode.getQualifierLength())
535f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		   )
536f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	    {
537f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			throw new XMPException("Mismatch between alias and base nodes",
538f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPError.BADXMP);
539f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
540f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
541f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		for (Iterator an = aliasNode.iterateChildren(),
542f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					  bn = baseNode.iterateChildren();
543f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			 an.hasNext() && bn.hasNext();)
544f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
545f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode aliasChild = (XMPNode) an.next();
546f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode baseChild =  (XMPNode) bn.next();
547f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			compareAliasedSubtrees (aliasChild, baseChild, false);
548f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
549f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
550f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
551f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		for (Iterator an = aliasNode.iterateQualifier(),
552f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					  bn = baseNode.iterateQualifier();
553f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			 an.hasNext() && bn.hasNext();)
554f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
555f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode aliasQual = (XMPNode) an.next();
556f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode baseQual =  (XMPNode) bn.next();
557f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			compareAliasedSubtrees (aliasQual, baseQual, false);
558f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
559f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
560f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
561f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
562f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
563f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * The initial support for WAV files mapped a legacy ID3 audio copyright
564f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * into a new xmpDM:copyright property. This is special case code to migrate
565f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * that into dc:rights['x-default']. The rules:
566f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
567f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * <pre>
568f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * 1. If there is no dc:rights array, or an empty array -
569f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *    Create one with dc:rights['x-default'] set from double linefeed and xmpDM:copyright.
570f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
571f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * 2. If there is a dc:rights array but it has no x-default item -
572f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *    Create an x-default item as a copy of the first item then apply rule #3.
573f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
574f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * 3. If there is a dc:rights array with an x-default item,
575f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *    Look for a double linefeed in the value.
576f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *     A. If no double linefeed, compare the x-default value to the xmpDM:copyright value.
577f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *         A1. If they match then leave the x-default value alone.
578f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *         A2. Otherwise, append a double linefeed and
579f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *             the xmpDM:copyright value to the x-default value.
580f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *     B. If there is a double linefeed, compare the trailing text to the xmpDM:copyright value.
581f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *         B1. If they match then leave the x-default value alone.
582f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *         B2. Otherwise, replace the trailing x-default text with the xmpDM:copyright value.
583f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
584f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * 4. In all cases, delete the xmpDM:copyright property.
585f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * </pre>
586f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 *
587f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param xmp the metadata object
588f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * @param dmCopyright the "dm:copyright"-property
589f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
590f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static void	migrateAudioCopyright (XMPMeta xmp, XMPNode dmCopyright)
591f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
592f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		try
593f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
594f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode dcSchema = XMPNodeUtils.findSchemaNode(
595f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				((XMPMetaImpl) xmp).getRoot(), XMPConst.NS_DC, true);
596f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
597f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			String dmValue = dmCopyright.getValue();
598f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			String doubleLF = "\n\n";
599f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
600f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			XMPNode dcRightsArray = XMPNodeUtils.findChildNode (dcSchema, "dc:rights", false);
601f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
602f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			if (dcRightsArray == null  ||  !dcRightsArray.hasChildren())
603f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
604f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				// 1. No dc:rights array, create from double linefeed and xmpDM:copyright.
605f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				dmValue = doubleLF + dmValue;
606f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				xmp.setLocalizedText(XMPConst.NS_DC, "rights", "", XMPConst.X_DEFAULT, dmValue,
607f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						null);
608f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
609f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			else
610f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			{
611f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				int xdIndex = XMPNodeUtils.lookupLanguageItem(dcRightsArray, XMPConst.X_DEFAULT);
612f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
613f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (xdIndex < 0)
614f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
615f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					// 2. No x-default item, create from the first item.
616f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					String firstValue = dcRightsArray.getChild(1).getValue();
617f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					xmp.setLocalizedText (XMPConst.NS_DC, "rights", "", XMPConst.X_DEFAULT,
618f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						firstValue, null);
619f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					xdIndex = XMPNodeUtils.lookupLanguageItem(dcRightsArray, XMPConst.X_DEFAULT);
620f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
621f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
622f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				// 3. Look for a double linefeed in the x-default value.
623f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				XMPNode defaultNode = dcRightsArray.getChild(xdIndex);
624f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				String defaultValue = defaultNode.getValue();
625f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				int lfPos = defaultValue.indexOf(doubleLF);
626f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
627f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				if (lfPos < 0)
628f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
629f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					// 3A. No double LF, compare whole values.
630f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					if (!dmValue.equals(defaultValue))
631f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					{
632f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						// 3A2. Append the xmpDM:copyright to the x-default
633f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						// item.
634f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						defaultNode.setValue(defaultValue + doubleLF + dmValue);
635f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					}
636f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
637f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				else
638f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				{
639f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					// 3B. Has double LF, compare the tail.
640f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					if (!defaultValue.substring(lfPos + 2).equals(dmValue))
641f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					{
642f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						// 3B2. Replace the x-default tail.
643f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling						defaultNode.setValue(defaultValue.substring(0, lfPos + 2) + dmValue);
644f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling					}
645f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling				}
646f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
647f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			}
648f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
649f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// 4. Get rid of the xmpDM:copyright.
650f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			dmCopyright.getParent().removeChild(dmCopyright);
651f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
652f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		catch (XMPException e)
653f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		{
654f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// Don't let failures (like a bad dc:rights form) stop other
655f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling			// cleanup.
656f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		}
657f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
658f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
659f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
660f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	/**
661f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * Initializes the map that contains the known arrays, that are fixed by
662f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 * {@link XMPNormalizer#normalizeDCArrays(XMPNode)}.
663f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	 */
664f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	private static void initDCArrays()
665f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	{
666f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		dcArrayForms = new HashMap();
667f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
668f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// Properties supposed to be a "Bag".
669f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		PropertyOptions bagForm = new PropertyOptions();
670f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		bagForm.setArray(true);
671f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		dcArrayForms.put("dc:contributor", bagForm);
672f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		dcArrayForms.put("dc:language", bagForm);
673f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		dcArrayForms.put("dc:publisher", bagForm);
674f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		dcArrayForms.put("dc:relation", bagForm);
675f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		dcArrayForms.put("dc:subject", bagForm);
676f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		dcArrayForms.put("dc:type", bagForm);
677f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
678f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// Properties supposed to be a "Seq".
679f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		PropertyOptions seqForm = new PropertyOptions();
680f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		seqForm.setArray(true);
681f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		seqForm.setArrayOrdered(true);
682f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		dcArrayForms.put("dc:creator", seqForm);
683f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		dcArrayForms.put("dc:date", seqForm);
684f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling
685f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		// Properties supposed to be an "Alt" in alternative-text form.
686f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		PropertyOptions altTextForm = new PropertyOptions();
687f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		altTextForm.setArray(true);
688f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		altTextForm.setArrayOrdered(true);
689f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		altTextForm.setArrayAlternate(true);
690f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		altTextForm.setArrayAltText(true);
691f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		dcArrayForms.put("dc:description", altTextForm);
692f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		dcArrayForms.put("dc:rights", altTextForm);
693f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling		dcArrayForms.put("dc:title", altTextForm);
694f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling	}
695f12f744843a67c910ec325fc6dfa73988f67b97cSascha Haeberling}
696