blob: 1b8430db65239be9e0556fde0162ec95e00cbe75 [file] [log] [blame]
// =================================================================================================
// ADOBE SYSTEMS INCORPORATED
// Copyright 2006 Adobe Systems Incorporated
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
// of the Adobe license agreement accompanying it.
// =================================================================================================
package com.adobe.xmp.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;
import com.adobe.xmp.XMPConst;
import com.adobe.xmp.XMPError;
import com.adobe.xmp.XMPException;
import com.adobe.xmp.XMPSchemaRegistry;
import com.adobe.xmp.options.AliasOptions;
import com.adobe.xmp.properties.XMPAliasInfo;
/**
* The schema registry handles the namespaces, aliases and global options for the XMP Toolkit. There
* is only one single instance used by the toolkit.
*
* @since 27.01.2006
*/
public final class XMPSchemaRegistryImpl implements XMPSchemaRegistry, XMPConst
{
/** a map from a namespace URI to its registered prefix */
private Map namespaceToPrefixMap = new HashMap();
/** a map from a prefix to the associated namespace URI */
private Map prefixToNamespaceMap = new HashMap();
/** a map of all registered aliases.
* The map is a relationship from a qname to an <code>XMPAliasInfo</code>-object. */
private Map aliasMap = new HashMap();
/** The pattern that must not be contained in simple properties */
private Pattern p = Pattern.compile("[/*?\\[\\]]");
/**
* Performs the initialisation of the registry with the default namespaces, aliases and global
* options.
*/
public XMPSchemaRegistryImpl()
{
try
{
registerStandardNamespaces();
registerStandardAliases();
}
catch (XMPException e)
{
throw new RuntimeException("The XMPSchemaRegistry cannot be initialized!");
}
}
// ---------------------------------------------------------------------------------------------
// Namespace Functions
/**
* @see XMPSchemaRegistry#registerNamespace(String, String)
*/
public synchronized String registerNamespace(String namespaceURI, String suggestedPrefix)
throws XMPException
{
ParameterAsserts.assertSchemaNS(namespaceURI);
ParameterAsserts.assertPrefix(suggestedPrefix);
if (suggestedPrefix.charAt(suggestedPrefix.length() - 1) != ':')
{
suggestedPrefix += ':';
}
if (!Utils.isXMLNameNS(suggestedPrefix.substring(0,
suggestedPrefix.length() - 1)))
{
throw new XMPException("The prefix is a bad XML name", XMPError.BADXML);
}
String registeredPrefix = (String) namespaceToPrefixMap.get(namespaceURI);
String registeredNS = (String) prefixToNamespaceMap.get(suggestedPrefix);
if (registeredPrefix != null)
{
// Return the actual prefix
return registeredPrefix;
}
else
{
if (registeredNS != null)
{
// the namespace is new, but the prefix is already engaged,
// we generate a new prefix out of the suggested
String generatedPrefix = suggestedPrefix;
for (int i = 1; prefixToNamespaceMap.containsKey(generatedPrefix); i++)
{
generatedPrefix = suggestedPrefix
.substring(0, suggestedPrefix.length() - 1)
+ "_" + i + "_:";
}
suggestedPrefix = generatedPrefix;
}
prefixToNamespaceMap.put(suggestedPrefix, namespaceURI);
namespaceToPrefixMap.put(namespaceURI, suggestedPrefix);
// Return the suggested prefix
return suggestedPrefix;
}
}
/**
* @see XMPSchemaRegistry#deleteNamespace(String)
*/
public synchronized void deleteNamespace(String namespaceURI)
{
String prefixToDelete = getNamespacePrefix(namespaceURI);
if (prefixToDelete != null)
{
namespaceToPrefixMap.remove(namespaceURI);
prefixToNamespaceMap.remove(prefixToDelete);
}
}
/**
* @see XMPSchemaRegistry#getNamespacePrefix(String)
*/
public synchronized String getNamespacePrefix(String namespaceURI)
{
return (String) namespaceToPrefixMap.get(namespaceURI);
}
/**
* @see XMPSchemaRegistry#getNamespaceURI(String)
*/
public synchronized String getNamespaceURI(String namespacePrefix)
{
if (namespacePrefix != null && !namespacePrefix.endsWith(":"))
{
namespacePrefix += ":";
}
return (String) prefixToNamespaceMap.get(namespacePrefix);
}
/**
* @see XMPSchemaRegistry#getNamespaces()
*/
public synchronized Map getNamespaces()
{
return Collections.unmodifiableMap(new TreeMap(namespaceToPrefixMap));
}
/**
* @see XMPSchemaRegistry#getPrefixes()
*/
public synchronized Map getPrefixes()
{
return Collections.unmodifiableMap(new TreeMap(prefixToNamespaceMap));
}
/**
* Register the standard namespaces of schemas and types that are included in the XMP
* Specification and some other Adobe private namespaces.
* Note: This method is not lock because only called by the constructor.
*
* @throws XMPException Forwards processing exceptions
*/
private void registerStandardNamespaces() throws XMPException
{
// register standard namespaces
registerNamespace(NS_XML, "xml");
registerNamespace(NS_RDF, "rdf");
registerNamespace(NS_DC, "dc");
registerNamespace(NS_IPTCCORE, "Iptc4xmpCore");
// register Adobe standard namespaces
registerNamespace(NS_X, "x");
registerNamespace(NS_IX, "iX");
registerNamespace(NS_XMP, "xmp");
registerNamespace(NS_XMP_RIGHTS, "xmpRights");
registerNamespace(NS_XMP_MM, "xmpMM");
registerNamespace(NS_XMP_BJ, "xmpBJ");
registerNamespace(NS_XMP_NOTE, "xmpNote");
registerNamespace(NS_PDF, "pdf");
registerNamespace(NS_PDFX, "pdfx");
registerNamespace(NS_PDFX_ID, "pdfxid");
registerNamespace(NS_PDFA_SCHEMA, "pdfaSchema");
registerNamespace(NS_PDFA_PROPERTY, "pdfaProperty");
registerNamespace(NS_PDFA_TYPE, "pdfaType");
registerNamespace(NS_PDFA_FIELD, "pdfaField");
registerNamespace(NS_PDFA_ID, "pdfaid");
registerNamespace(NS_PDFA_EXTENSION, "pdfaExtension");
registerNamespace(NS_PHOTOSHOP, "photoshop");
registerNamespace(NS_PSALBUM, "album");
registerNamespace(NS_EXIF, "exif");
registerNamespace(NS_EXIF_AUX, "aux");
registerNamespace(NS_TIFF, "tiff");
registerNamespace(NS_PNG, "png");
registerNamespace(NS_JPEG, "jpeg");
registerNamespace(NS_JP2K, "jp2k");
registerNamespace(NS_CAMERARAW, "crs");
registerNamespace(NS_ADOBESTOCKPHOTO, "bmsp");
registerNamespace(NS_CREATOR_ATOM, "creatorAtom");
registerNamespace(NS_ASF, "asf");
registerNamespace(NS_WAV, "wav");
// register Adobe private namespaces
registerNamespace(NS_DM, "xmpDM");
registerNamespace(NS_TRANSIENT, "xmpx");
// register Adobe standard type namespaces
registerNamespace(TYPE_TEXT, "xmpT");
registerNamespace(TYPE_PAGEDFILE, "xmpTPg");
registerNamespace(TYPE_GRAPHICS, "xmpG");
registerNamespace(TYPE_IMAGE, "xmpGImg");
registerNamespace(TYPE_FONT, "stFNT");
registerNamespace(TYPE_DIMENSIONS, "stDim");
registerNamespace(TYPE_RESOURCEEVENT, "stEvt");
registerNamespace(TYPE_RESOURCEREF, "stRef");
registerNamespace(TYPE_ST_VERSION, "stVer");
registerNamespace(TYPE_ST_JOB, "stJob");
registerNamespace(TYPE_MANIFESTITEM, "stMfs");
registerNamespace(TYPE_IDENTIFIERQUAL, "xmpidq");
}
// ---------------------------------------------------------------------------------------------
// Alias Functions
/**
* @see XMPSchemaRegistry#resolveAlias(String, String)
*/
public synchronized XMPAliasInfo resolveAlias(String aliasNS, String aliasProp)
{
String aliasPrefix = getNamespacePrefix(aliasNS);
if (aliasPrefix == null)
{
return null;
}
return (XMPAliasInfo) aliasMap.get(aliasPrefix + aliasProp);
}
/**
* @see XMPSchemaRegistry#findAlias(java.lang.String)
*/
public synchronized XMPAliasInfo findAlias(String qname)
{
return (XMPAliasInfo) aliasMap.get(qname);
}
/**
* @see XMPSchemaRegistry#findAliases(String)
*/
public synchronized XMPAliasInfo[] findAliases(String aliasNS)
{
String prefix = getNamespacePrefix(aliasNS);
List result = new ArrayList();
if (prefix != null)
{
for (Iterator it = aliasMap.keySet().iterator(); it.hasNext();)
{
String qname = (String) it.next();
if (qname.startsWith(prefix))
{
result.add(findAlias(qname));
}
}
}
return (XMPAliasInfo[]) result.toArray(new XMPAliasInfo[result.size()]);
}
/**
* Associates an alias name with an actual name.
* <p>
* Define a alias mapping from one namespace/property to another. Both
* property names must be simple names. An alias can be a direct mapping,
* where the alias and actual have the same data type. It is also possible
* to map a simple alias to an item in an array. This can either be to the
* first item in the array, or to the 'x-default' item in an alt-text array.
* Multiple alias names may map to the same actual, as long as the forms
* match. It is a no-op to reregister an alias in an identical fashion.
* Note: This method is not locking because only called by registerStandardAliases
* which is only called by the constructor.
* Note2: The method is only package-private so that it can be tested with unittests
*
* @param aliasNS
* The namespace URI for the alias. Must not be null or the empty
* string.
* @param aliasProp
* The name of the alias. Must be a simple name, not null or the
* empty string and not a general path expression.
* @param actualNS
* The namespace URI for the actual. Must not be null or the
* empty string.
* @param actualProp
* The name of the actual. Must be a simple name, not null or the
* empty string and not a general path expression.
* @param aliasForm
* Provides options for aliases for simple aliases to array
* items. This is needed to know what kind of array to create if
* set for the first time via the simple alias. Pass
* <code>XMP_NoOptions</code>, the default value, for all
* direct aliases regardless of whether the actual data type is
* an array or not (see {@link AliasOptions}).
* @throws XMPException
* for inconsistant aliases.
*/
synchronized void registerAlias(String aliasNS, String aliasProp, final String actualNS,
final String actualProp, final AliasOptions aliasForm) throws XMPException
{
ParameterAsserts.assertSchemaNS(aliasNS);
ParameterAsserts.assertPropName(aliasProp);
ParameterAsserts.assertSchemaNS(actualNS);
ParameterAsserts.assertPropName(actualProp);
// Fix the alias options
final AliasOptions aliasOpts = aliasForm != null ?
new AliasOptions(XMPNodeUtils.verifySetOptions(
aliasForm.toPropertyOptions(), null).getOptions()) :
new AliasOptions();
if (p.matcher(aliasProp).find() || p.matcher(actualProp).find())
{
throw new XMPException("Alias and actual property names must be simple",
XMPError.BADXPATH);
}
// check if both namespaces are registered
final String aliasPrefix = getNamespacePrefix(aliasNS);
final String actualPrefix = getNamespacePrefix(actualNS);
if (aliasPrefix == null)
{
throw new XMPException("Alias namespace is not registered", XMPError.BADSCHEMA);
}
else if (actualPrefix == null)
{
throw new XMPException("Actual namespace is not registered",
XMPError.BADSCHEMA);
}
String key = aliasPrefix + aliasProp;
// check if alias is already existing
if (aliasMap.containsKey(key))
{
throw new XMPException("Alias is already existing", XMPError.BADPARAM);
}
else if (aliasMap.containsKey(actualPrefix + actualProp))
{
throw new XMPException(
"Actual property is already an alias, use the base property",
XMPError.BADPARAM);
}
XMPAliasInfo aliasInfo = new XMPAliasInfo()
{
/**
* @see XMPAliasInfo#getNamespace()
*/
public String getNamespace()
{
return actualNS;
}
/**
* @see XMPAliasInfo#getPrefix()
*/
public String getPrefix()
{
return actualPrefix;
}
/**
* @see XMPAliasInfo#getPropName()
*/
public String getPropName()
{
return actualProp;
}
/**
* @see XMPAliasInfo#getAliasForm()
*/
public AliasOptions getAliasForm()
{
return aliasOpts;
}
public String toString()
{
return actualPrefix + actualProp + " NS(" + actualNS + "), FORM ("
+ getAliasForm() + ")";
}
};
aliasMap.put(key, aliasInfo);
}
/**
* @see XMPSchemaRegistry#getAliases()
*/
public synchronized Map getAliases()
{
return Collections.unmodifiableMap(new TreeMap(aliasMap));
}
/**
* Register the standard aliases.
* Note: This method is not lock because only called by the constructor.
*
* @throws XMPException If the registrations of at least one alias fails.
*/
private void registerStandardAliases() throws XMPException
{
AliasOptions aliasToArrayOrdered = new AliasOptions().setArrayOrdered(true);
AliasOptions aliasToArrayAltText = new AliasOptions().setArrayAltText(true);
// Aliases from XMP to DC.
registerAlias(NS_XMP, "Author", NS_DC, "creator", aliasToArrayOrdered);
registerAlias(NS_XMP, "Authors", NS_DC, "creator", null);
registerAlias(NS_XMP, "Description", NS_DC, "description", null);
registerAlias(NS_XMP, "Format", NS_DC, "format", null);
registerAlias(NS_XMP, "Keywords", NS_DC, "subject", null);
registerAlias(NS_XMP, "Locale", NS_DC, "language", null);
registerAlias(NS_XMP, "Title", NS_DC, "title", null);
registerAlias(NS_XMP_RIGHTS, "Copyright", NS_DC, "rights", null);
// Aliases from PDF to DC and XMP.
registerAlias(NS_PDF, "Author", NS_DC, "creator", aliasToArrayOrdered);
registerAlias(NS_PDF, "BaseURL", NS_XMP, "BaseURL", null);
registerAlias(NS_PDF, "CreationDate", NS_XMP, "CreateDate", null);
registerAlias(NS_PDF, "Creator", NS_XMP, "CreatorTool", null);
registerAlias(NS_PDF, "ModDate", NS_XMP, "ModifyDate", null);
registerAlias(NS_PDF, "Subject", NS_DC, "description", aliasToArrayAltText);
registerAlias(NS_PDF, "Title", NS_DC, "title", aliasToArrayAltText);
// Aliases from PHOTOSHOP to DC and XMP.
registerAlias(NS_PHOTOSHOP, "Author", NS_DC, "creator", aliasToArrayOrdered);
registerAlias(NS_PHOTOSHOP, "Caption", NS_DC, "description", aliasToArrayAltText);
registerAlias(NS_PHOTOSHOP, "Copyright", NS_DC, "rights", aliasToArrayAltText);
registerAlias(NS_PHOTOSHOP, "Keywords", NS_DC, "subject", null);
registerAlias(NS_PHOTOSHOP, "Marked", NS_XMP_RIGHTS, "Marked", null);
registerAlias(NS_PHOTOSHOP, "Title", NS_DC, "title", aliasToArrayAltText);
registerAlias(NS_PHOTOSHOP, "WebStatement", NS_XMP_RIGHTS, "WebStatement", null);
// Aliases from TIFF and EXIF to DC and XMP.
registerAlias(NS_TIFF, "Artist", NS_DC, "creator", aliasToArrayOrdered);
registerAlias(NS_TIFF, "Copyright", NS_DC, "rights", null);
registerAlias(NS_TIFF, "DateTime", NS_XMP, "ModifyDate", null);
registerAlias(NS_TIFF, "ImageDescription", NS_DC, "description", null);
registerAlias(NS_TIFF, "Software", NS_XMP, "CreatorTool", null);
// Aliases from PNG (Acrobat ImageCapture) to DC and XMP.
registerAlias(NS_PNG, "Author", NS_DC, "creator", aliasToArrayOrdered);
registerAlias(NS_PNG, "Copyright", NS_DC, "rights", aliasToArrayAltText);
registerAlias(NS_PNG, "CreationTime", NS_XMP, "CreateDate", null);
registerAlias(NS_PNG, "Description", NS_DC, "description", aliasToArrayAltText);
registerAlias(NS_PNG, "ModificationTime", NS_XMP, "ModifyDate", null);
registerAlias(NS_PNG, "Software", NS_XMP, "CreatorTool", null);
registerAlias(NS_PNG, "Title", NS_DC, "title", aliasToArrayAltText);
}
}