• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // =================================================================================================
2 // ADOBE SYSTEMS INCORPORATED
3 // Copyright 2006 Adobe Systems Incorporated
4 // All Rights Reserved
5 //
6 // NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the terms
7 // of the Adobe license agreement accompanying it.
8 // =================================================================================================
9 
10 package com.adobe.xmp.impl;
11 
12 import java.util.ArrayList;
13 import java.util.Collections;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.TreeMap;
19 import java.util.regex.Pattern;
20 
21 import com.adobe.xmp.XMPConst;
22 import com.adobe.xmp.XMPError;
23 import com.adobe.xmp.XMPException;
24 import com.adobe.xmp.XMPSchemaRegistry;
25 import com.adobe.xmp.options.AliasOptions;
26 import com.adobe.xmp.properties.XMPAliasInfo;
27 
28 
29 /**
30  * The schema registry handles the namespaces, aliases and global options for the XMP Toolkit. There
31  * is only one single instance used by the toolkit.
32  *
33  * @since 27.01.2006
34  */
35 public final class XMPSchemaRegistryImpl implements XMPSchemaRegistry, XMPConst
36 {
37 	/** a map from a namespace URI to its registered prefix */
38 	private Map namespaceToPrefixMap = new HashMap();
39 
40 	/** a map from a prefix to the associated namespace URI */
41 	private Map prefixToNamespaceMap = new HashMap();
42 
43 	/** a map of all registered aliases.
44 	 *  The map is a relationship from a qname to an <code>XMPAliasInfo</code>-object. */
45 	private Map aliasMap = new HashMap();
46 	/** The pattern that must not be contained in simple properties */
47 	private Pattern p = Pattern.compile("[/*?\\[\\]]");
48 
49 
50 	/**
51 	 * Performs the initialisation of the registry with the default namespaces, aliases and global
52 	 * options.
53 	 */
XMPSchemaRegistryImpl()54 	public XMPSchemaRegistryImpl()
55 	{
56 		try
57 		{
58 			registerStandardNamespaces();
59 			registerStandardAliases();
60 		}
61 		catch (XMPException e)
62 		{
63 			throw new RuntimeException("The XMPSchemaRegistry cannot be initialized!");
64 		}
65 	}
66 
67 
68 	// ---------------------------------------------------------------------------------------------
69 	// Namespace Functions
70 
71 
72 	/**
73 	 * @see XMPSchemaRegistry#registerNamespace(String, String)
74 	 */
registerNamespace(String namespaceURI, String suggestedPrefix)75 	public synchronized String registerNamespace(String namespaceURI, String suggestedPrefix)
76 			throws XMPException
77 	{
78 		ParameterAsserts.assertSchemaNS(namespaceURI);
79 		ParameterAsserts.assertPrefix(suggestedPrefix);
80 
81 		if (suggestedPrefix.charAt(suggestedPrefix.length() - 1) != ':')
82 		{
83 			suggestedPrefix += ':';
84 		}
85 
86 		if (!Utils.isXMLNameNS(suggestedPrefix.substring(0,
87 				suggestedPrefix.length() - 1)))
88 		{
89 			throw new XMPException("The prefix is a bad XML name", XMPError.BADXML);
90 		}
91 
92 		String registeredPrefix = (String) namespaceToPrefixMap.get(namespaceURI);
93 		String registeredNS = (String) prefixToNamespaceMap.get(suggestedPrefix);
94 		if (registeredPrefix != null)
95 		{
96 			// Return the actual prefix
97 			return registeredPrefix;
98 		}
99 		else
100 		{
101 			if (registeredNS != null)
102 			{
103 				// the namespace is new, but the prefix is already engaged,
104 				// we generate a new prefix out of the suggested
105 				String generatedPrefix = suggestedPrefix;
106 				for (int i = 1; prefixToNamespaceMap.containsKey(generatedPrefix); i++)
107 				{
108 					generatedPrefix = suggestedPrefix
109 							.substring(0, suggestedPrefix.length() - 1)
110 							+ "_" + i + "_:";
111 				}
112 				suggestedPrefix = generatedPrefix;
113 			}
114 			prefixToNamespaceMap.put(suggestedPrefix, namespaceURI);
115 			namespaceToPrefixMap.put(namespaceURI, suggestedPrefix);
116 
117 			// Return the suggested prefix
118 			return suggestedPrefix;
119 		}
120 	}
121 
122 
123 	/**
124 	 * @see XMPSchemaRegistry#deleteNamespace(String)
125 	 */
deleteNamespace(String namespaceURI)126 	public synchronized void deleteNamespace(String namespaceURI)
127 	{
128 		String prefixToDelete = getNamespacePrefix(namespaceURI);
129 		if (prefixToDelete != null)
130 		{
131 			namespaceToPrefixMap.remove(namespaceURI);
132 			prefixToNamespaceMap.remove(prefixToDelete);
133 		}
134 	}
135 
136 
137 	/**
138 	 * @see XMPSchemaRegistry#getNamespacePrefix(String)
139 	 */
getNamespacePrefix(String namespaceURI)140 	public synchronized String getNamespacePrefix(String namespaceURI)
141 	{
142 		return (String) namespaceToPrefixMap.get(namespaceURI);
143 	}
144 
145 
146 	/**
147 	 * @see XMPSchemaRegistry#getNamespaceURI(String)
148 	 */
getNamespaceURI(String namespacePrefix)149 	public synchronized String getNamespaceURI(String namespacePrefix)
150 	{
151 		if (namespacePrefix != null  &&  !namespacePrefix.endsWith(":"))
152 		{
153 			namespacePrefix += ":";
154 		}
155 		return (String) prefixToNamespaceMap.get(namespacePrefix);
156 	}
157 
158 
159 	/**
160 	 * @see XMPSchemaRegistry#getNamespaces()
161 	 */
getNamespaces()162 	public synchronized Map getNamespaces()
163 	{
164 		return Collections.unmodifiableMap(new TreeMap(namespaceToPrefixMap));
165 	}
166 
167 
168 	/**
169 	 * @see XMPSchemaRegistry#getPrefixes()
170 	 */
getPrefixes()171 	public synchronized Map getPrefixes()
172 	{
173 		return Collections.unmodifiableMap(new TreeMap(prefixToNamespaceMap));
174 	}
175 
176 
177 	/**
178 	 * Register the standard namespaces of schemas and types that are included in the XMP
179 	 * Specification and some other Adobe private namespaces.
180 	 * Note: This method is not lock because only called by the constructor.
181 	 *
182 	 * @throws XMPException Forwards processing exceptions
183 	 */
registerStandardNamespaces()184 	private void registerStandardNamespaces() throws XMPException
185 	{
186 		// register standard namespaces
187 		registerNamespace(NS_XML, "xml");
188 		registerNamespace(NS_RDF, "rdf");
189 		registerNamespace(NS_DC, "dc");
190 		registerNamespace(NS_IPTCCORE, "Iptc4xmpCore");
191 
192 		// register Adobe standard namespaces
193 		registerNamespace(NS_X, "x");
194 		registerNamespace(NS_IX, "iX");
195 
196 		registerNamespace(NS_XMP, "xmp");
197 		registerNamespace(NS_XMP_RIGHTS, "xmpRights");
198 		registerNamespace(NS_XMP_MM, "xmpMM");
199 		registerNamespace(NS_XMP_BJ, "xmpBJ");
200 		registerNamespace(NS_XMP_NOTE, "xmpNote");
201 
202 		registerNamespace(NS_PDF, "pdf");
203 		registerNamespace(NS_PDFX, "pdfx");
204 		registerNamespace(NS_PDFX_ID, "pdfxid");
205 		registerNamespace(NS_PDFA_SCHEMA, "pdfaSchema");
206 		registerNamespace(NS_PDFA_PROPERTY, "pdfaProperty");
207 		registerNamespace(NS_PDFA_TYPE, "pdfaType");
208 		registerNamespace(NS_PDFA_FIELD, "pdfaField");
209 		registerNamespace(NS_PDFA_ID, "pdfaid");
210 		registerNamespace(NS_PDFA_EXTENSION, "pdfaExtension");
211 		registerNamespace(NS_PHOTOSHOP, "photoshop");
212 		registerNamespace(NS_PSALBUM, "album");
213 		registerNamespace(NS_EXIF, "exif");
214 		registerNamespace(NS_EXIF_AUX, "aux");
215 		registerNamespace(NS_TIFF, "tiff");
216 		registerNamespace(NS_PNG, "png");
217 		registerNamespace(NS_JPEG, "jpeg");
218 		registerNamespace(NS_JP2K, "jp2k");
219 		registerNamespace(NS_CAMERARAW, "crs");
220 		registerNamespace(NS_ADOBESTOCKPHOTO, "bmsp");
221 		registerNamespace(NS_CREATOR_ATOM, "creatorAtom");
222 		registerNamespace(NS_ASF, "asf");
223 		registerNamespace(NS_WAV, "wav");
224 
225 		// register Adobe private namespaces
226 		registerNamespace(NS_DM, "xmpDM");
227 		registerNamespace(NS_TRANSIENT, "xmpx");
228 
229 		// register Adobe standard type namespaces
230 		registerNamespace(TYPE_TEXT, "xmpT");
231 		registerNamespace(TYPE_PAGEDFILE, "xmpTPg");
232 		registerNamespace(TYPE_GRAPHICS, "xmpG");
233 		registerNamespace(TYPE_IMAGE, "xmpGImg");
234 		registerNamespace(TYPE_FONT, "stFNT");
235 		registerNamespace(TYPE_DIMENSIONS, "stDim");
236 		registerNamespace(TYPE_RESOURCEEVENT, "stEvt");
237 		registerNamespace(TYPE_RESOURCEREF, "stRef");
238 		registerNamespace(TYPE_ST_VERSION, "stVer");
239 		registerNamespace(TYPE_ST_JOB, "stJob");
240 		registerNamespace(TYPE_MANIFESTITEM, "stMfs");
241 		registerNamespace(TYPE_IDENTIFIERQUAL, "xmpidq");
242 	}
243 
244 
245 
246 	// ---------------------------------------------------------------------------------------------
247 	// Alias Functions
248 
249 
250 	/**
251 	 * @see XMPSchemaRegistry#resolveAlias(String, String)
252 	 */
resolveAlias(String aliasNS, String aliasProp)253 	public synchronized XMPAliasInfo resolveAlias(String aliasNS, String aliasProp)
254 	{
255 		String aliasPrefix = getNamespacePrefix(aliasNS);
256 		if (aliasPrefix == null)
257 		{
258 			return null;
259 		}
260 
261 		return (XMPAliasInfo) aliasMap.get(aliasPrefix + aliasProp);
262 	}
263 
264 
265 	/**
266 	 * @see XMPSchemaRegistry#findAlias(java.lang.String)
267 	 */
findAlias(String qname)268 	public synchronized XMPAliasInfo findAlias(String qname)
269 	{
270 		return (XMPAliasInfo) aliasMap.get(qname);
271 	}
272 
273 
274 	/**
275 	 * @see XMPSchemaRegistry#findAliases(String)
276 	 */
findAliases(String aliasNS)277 	public synchronized XMPAliasInfo[] findAliases(String aliasNS)
278 	{
279 		String prefix = getNamespacePrefix(aliasNS);
280 		List result = new ArrayList();
281 		if (prefix != null)
282 		{
283 			for (Iterator it = aliasMap.keySet().iterator(); it.hasNext();)
284 			{
285 				String qname = (String) it.next();
286 				if (qname.startsWith(prefix))
287 				{
288 					result.add(findAlias(qname));
289 				}
290 			}
291 
292 		}
293 		return (XMPAliasInfo[]) result.toArray(new XMPAliasInfo[result.size()]);
294 	}
295 
296 
297 	/**
298 	 * Associates an alias name with an actual name.
299 	 * <p>
300 	 * Define a alias mapping from one namespace/property to another. Both
301 	 * property names must be simple names. An alias can be a direct mapping,
302 	 * where the alias and actual have the same data type. It is also possible
303 	 * to map a simple alias to an item in an array. This can either be to the
304 	 * first item in the array, or to the 'x-default' item in an alt-text array.
305 	 * Multiple alias names may map to the same actual, as long as the forms
306 	 * match. It is a no-op to reregister an alias in an identical fashion.
307 	 * Note: This method is not locking because only called by registerStandardAliases
308 	 * which is only called by the constructor.
309 	 * Note2: The method is only package-private so that it can be tested with unittests
310 	 *
311 	 * @param aliasNS
312 	 *            The namespace URI for the alias. Must not be null or the empty
313 	 *            string.
314 	 * @param aliasProp
315 	 *            The name of the alias. Must be a simple name, not null or the
316 	 *            empty string and not a general path expression.
317 	 * @param actualNS
318 	 *            The namespace URI for the actual. Must not be null or the
319 	 *            empty string.
320 	 * @param actualProp
321 	 *            The name of the actual. Must be a simple name, not null or the
322 	 *            empty string and not a general path expression.
323 	 * @param aliasForm
324 	 *            Provides options for aliases for simple aliases to array
325 	 *            items. This is needed to know what kind of array to create if
326 	 *            set for the first time via the simple alias. Pass
327 	 *            <code>XMP_NoOptions</code>, the default value, for all
328 	 *            direct aliases regardless of whether the actual data type is
329 	 *            an array or not (see {@link AliasOptions}).
330 	 * @throws XMPException
331 	 *             for inconsistant aliases.
332 	 */
registerAlias(String aliasNS, String aliasProp, final String actualNS, final String actualProp, final AliasOptions aliasForm)333 	synchronized void registerAlias(String aliasNS, String aliasProp, final String actualNS,
334 			final String actualProp, final AliasOptions aliasForm) throws XMPException
335 	{
336 		ParameterAsserts.assertSchemaNS(aliasNS);
337 		ParameterAsserts.assertPropName(aliasProp);
338 		ParameterAsserts.assertSchemaNS(actualNS);
339 		ParameterAsserts.assertPropName(actualProp);
340 
341 		// Fix the alias options
342 		final AliasOptions aliasOpts = aliasForm != null ?
343 			new AliasOptions(XMPNodeUtils.verifySetOptions(
344 				aliasForm.toPropertyOptions(), null).getOptions()) :
345 			new AliasOptions();
346 
347 		if (p.matcher(aliasProp).find()  ||  p.matcher(actualProp).find())
348 		{
349 			throw new XMPException("Alias and actual property names must be simple",
350 					XMPError.BADXPATH);
351 		}
352 
353 		// check if both namespaces are registered
354 		final String aliasPrefix = getNamespacePrefix(aliasNS);
355 		final String actualPrefix = getNamespacePrefix(actualNS);
356 		if (aliasPrefix == null)
357 		{
358 			throw new XMPException("Alias namespace is not registered", XMPError.BADSCHEMA);
359 		}
360 		else if (actualPrefix == null)
361 		{
362 			throw new XMPException("Actual namespace is not registered",
363 				XMPError.BADSCHEMA);
364 		}
365 
366 		String key = aliasPrefix + aliasProp;
367 
368 		// check if alias is already existing
369 		if (aliasMap.containsKey(key))
370 		{
371 			throw new XMPException("Alias is already existing", XMPError.BADPARAM);
372 		}
373 		else if (aliasMap.containsKey(actualPrefix + actualProp))
374 		{
375 			throw new XMPException(
376 					"Actual property is already an alias, use the base property",
377 					XMPError.BADPARAM);
378 		}
379 
380 		XMPAliasInfo aliasInfo = new XMPAliasInfo()
381 		{
382 			/**
383 			 * @see XMPAliasInfo#getNamespace()
384 			 */
385 			public String getNamespace()
386 			{
387 				return actualNS;
388 			}
389 
390 			/**
391 			 * @see XMPAliasInfo#getPrefix()
392 			 */
393 			public String getPrefix()
394 			{
395 				return actualPrefix;
396 			}
397 
398 			/**
399 			 * @see XMPAliasInfo#getPropName()
400 			 */
401 			public String getPropName()
402 			{
403 				return actualProp;
404 			}
405 
406 			/**
407 			 * @see XMPAliasInfo#getAliasForm()
408 			 */
409 			public AliasOptions getAliasForm()
410 			{
411 				return aliasOpts;
412 			}
413 
414 			public String toString()
415 			{
416 				return actualPrefix + actualProp + " NS(" + actualNS + "), FORM ("
417 						+ getAliasForm() + ")";
418 			}
419 		};
420 
421 		aliasMap.put(key, aliasInfo);
422 	}
423 
424 
425 	/**
426 	 * @see XMPSchemaRegistry#getAliases()
427 	 */
getAliases()428 	public synchronized Map getAliases()
429 	{
430 		return Collections.unmodifiableMap(new TreeMap(aliasMap));
431 	}
432 
433 
434 	/**
435 	 * Register the standard aliases.
436 	 * Note: This method is not lock because only called by the constructor.
437 	 *
438 	 * @throws XMPException If the registrations of at least one alias fails.
439 	 */
registerStandardAliases()440 	private void registerStandardAliases() throws XMPException
441 	{
442 		AliasOptions aliasToArrayOrdered = new AliasOptions().setArrayOrdered(true);
443 		AliasOptions aliasToArrayAltText = new AliasOptions().setArrayAltText(true);
444 
445 
446 		// Aliases from XMP to DC.
447 		registerAlias(NS_XMP, "Author", NS_DC, "creator", aliasToArrayOrdered);
448 		registerAlias(NS_XMP, "Authors", NS_DC, "creator", null);
449 		registerAlias(NS_XMP, "Description", NS_DC, "description", null);
450 		registerAlias(NS_XMP, "Format", NS_DC, "format", null);
451 		registerAlias(NS_XMP, "Keywords", NS_DC, "subject", null);
452 		registerAlias(NS_XMP, "Locale", NS_DC, "language", null);
453 		registerAlias(NS_XMP, "Title", NS_DC, "title", null);
454 		registerAlias(NS_XMP_RIGHTS, "Copyright", NS_DC, "rights", null);
455 
456 		// Aliases from PDF to DC and XMP.
457 		registerAlias(NS_PDF, "Author", NS_DC, "creator", aliasToArrayOrdered);
458 		registerAlias(NS_PDF, "BaseURL", NS_XMP, "BaseURL", null);
459 		registerAlias(NS_PDF, "CreationDate", NS_XMP, "CreateDate", null);
460 		registerAlias(NS_PDF, "Creator", NS_XMP, "CreatorTool", null);
461 		registerAlias(NS_PDF, "ModDate", NS_XMP, "ModifyDate", null);
462 		registerAlias(NS_PDF, "Subject", NS_DC, "description", aliasToArrayAltText);
463 		registerAlias(NS_PDF, "Title", NS_DC, "title", aliasToArrayAltText);
464 
465 		// Aliases from PHOTOSHOP to DC and XMP.
466 		registerAlias(NS_PHOTOSHOP, "Author", NS_DC, "creator", aliasToArrayOrdered);
467 		registerAlias(NS_PHOTOSHOP, "Caption", NS_DC, "description", aliasToArrayAltText);
468 		registerAlias(NS_PHOTOSHOP, "Copyright", NS_DC, "rights", aliasToArrayAltText);
469 		registerAlias(NS_PHOTOSHOP, "Keywords", NS_DC, "subject", null);
470 		registerAlias(NS_PHOTOSHOP, "Marked", NS_XMP_RIGHTS, "Marked", null);
471 		registerAlias(NS_PHOTOSHOP, "Title", NS_DC, "title", aliasToArrayAltText);
472 		registerAlias(NS_PHOTOSHOP, "WebStatement", NS_XMP_RIGHTS, "WebStatement", null);
473 
474 		// Aliases from TIFF and EXIF to DC and XMP.
475 		registerAlias(NS_TIFF, "Artist", NS_DC, "creator", aliasToArrayOrdered);
476 		registerAlias(NS_TIFF, "Copyright", NS_DC, "rights", null);
477 		registerAlias(NS_TIFF, "DateTime", NS_XMP, "ModifyDate", null);
478 		registerAlias(NS_TIFF, "ImageDescription", NS_DC, "description", null);
479 		registerAlias(NS_TIFF, "Software", NS_XMP, "CreatorTool", null);
480 
481 		// Aliases from PNG (Acrobat ImageCapture) to DC and XMP.
482 		registerAlias(NS_PNG, "Author", NS_DC, "creator", aliasToArrayOrdered);
483 		registerAlias(NS_PNG, "Copyright", NS_DC, "rights", aliasToArrayAltText);
484 		registerAlias(NS_PNG, "CreationTime", NS_XMP, "CreateDate", null);
485 		registerAlias(NS_PNG, "Description", NS_DC, "description", aliasToArrayAltText);
486 		registerAlias(NS_PNG, "ModificationTime", NS_XMP, "ModifyDate", null);
487 		registerAlias(NS_PNG, "Software", NS_XMP, "CreatorTool", null);
488 		registerAlias(NS_PNG, "Title", NS_DC, "title", aliasToArrayAltText);
489 	}
490 }