com.adobe.internal.xmp.XMPUtils Maven / Gradle / Ivy
Show all versions of xmpcore Show documentation
// =================================================================================================
// 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.internal.xmp;
import java.security.NoSuchAlgorithmException;
import com.adobe.internal.xmp.impl.Base64;
import com.adobe.internal.xmp.impl.ISO8601Converter;
import com.adobe.internal.xmp.impl.XMPUtilsImpl;
import com.adobe.internal.xmp.options.PropertyOptions;
import com.adobe.internal.xmp.options.TemplateOptions;
/**
* Utility methods for XMP. I included only those that are different from the
* Java default conversion utilities.
*
* @author Stefan Makswit
* @version $Revision$
* @since 21.02.2006
*/
public class XMPUtils
{
/** Private constructor */
private XMPUtils()
{
// EMPTY
}
/**
* Create a single edit string from an array of strings.
*
* @param xmp
* The XMP object containing the array to be catenated.
* @param schemaNS
* The schema namespace URI for the array. Must not be null or
* the empty string.
* @param arrayName
* The name of the array. May be a general path expression, must
* not be null or the empty string. Each item in the array must
* be a simple string value.
* @param separator
* The string to be used to separate the items in the catenated
* string. Defaults to "; ", ASCII semicolon and space
* (U+003B, U+0020).
* @param quotes
* The characters to be used as quotes around array items that
* contain a separator. Defaults to '"'
* @param allowCommas
* Option flag to control the catenation.
* @return Returns the string containing the catenated array items.
* @throws XMPException Forwards the Exceptions from the metadata processing
*/
public static String catenateArrayItems(XMPMeta xmp, String schemaNS, String arrayName,
String separator, String quotes, boolean allowCommas) throws XMPException
{
return XMPUtilsImpl
.catenateArrayItems(xmp, schemaNS, arrayName, separator, quotes, allowCommas);
}
/**
* Separate a single edit string into an array of strings.
*
* @param xmp
* The XMP object containing the array to be updated.
* @param schemaNS
* The schema namespace URI for the array. Must not be null or
* the empty string.
* @param arrayName
* The name of the array. May be a general path expression, must
* not be null or the empty string. Each item in the array must
* be a simple string value.
* @param catedStr
* The string to be separated into the array items.
* @param arrayOptions Option flags to control the separation.
* @param preserveCommas Flag if commas shall be preserved
* @throws XMPException Forwards the Exceptions from the metadata processing
*/
public static void separateArrayItems(XMPMeta xmp, String schemaNS, String arrayName,
String catedStr, PropertyOptions arrayOptions, boolean preserveCommas)
throws XMPException
{
XMPUtilsImpl.separateArrayItems(xmp, schemaNS, arrayName, catedStr, arrayOptions,
preserveCommas);
}
/**
* Remove multiple properties from an XMP object.
*
* RemoveProperties was created to support the File Info dialog's Delete
* button, and has been been generalized somewhat from those specific needs.
* It operates in one of three main modes depending on the schemaNS and
* propName parameters:
*
*
* - Non-empty
schemaNS
and propName
- The named property is
* removed if it is an external property, or if the
* flag doAllProperties
option is true. It does not matter whether the
* named property is an actual property or an alias.
*
* - Non-empty
schemaNS
and empty propName
- The all external
* properties in the named schema are removed. Internal properties are also
* removed if the flag doAllProperties
option is set. In addition,
* aliases from the named schema will be removed if the flag includeAliases
* option is set.
*
* - Empty
schemaNS
and empty propName
- All external properties in
* all schema are removed. Internal properties are also removed if the
* flag doAllProperties
option is passed. Aliases are implicitly handled
* because the associated actuals are internal if the alias is.
*
*
* It is an error to pass an empty schemaNS
and non-empty propName
.
*
* @param xmp
* The XMP object containing the properties to be removed.
*
* @param schemaNS
* Optional schema namespace URI for the properties to be
* removed.
*
* @param propName
* Optional path expression for the property to be removed.
*
* @param doAllProperties Option flag to control the deletion: do internal properties in
* addition to external properties.
*
* @param includeAliases Option flag to control the deletion:
* Include aliases in the "named schema" case above.
* Note: Currently not supported.
* @throws XMPException Forwards the Exceptions from the metadata processing
*/
public static void removeProperties(XMPMeta xmp, String schemaNS, String propName,
boolean doAllProperties, boolean includeAliases) throws XMPException
{
XMPUtilsImpl.removeProperties(xmp, schemaNS, propName, doAllProperties, includeAliases);
}
/**
* Alias without the new option deleteEmptyValues
.
* @param source The source XMP object.
* @param dest The destination XMP object.
* @param doAllProperties Do internal properties in addition to external properties.
* @param replaceOldValues Replace the values of existing properties.
* @throws XMPException Forwards the Exceptions from the metadata processing
*/
public static void appendProperties(XMPMeta source, XMPMeta dest, boolean doAllProperties,
boolean replaceOldValues) throws XMPException
{
appendProperties(source, dest, doAllProperties, replaceOldValues, false);
}
/**
* Append properties from one XMP object to another.
*
*
XMPUtils#appendProperties was created to support the File Info dialog's Append button, and
* has been been generalized somewhat from those specific needs. It appends information from one
* XMP object (source) to another (dest). The default operation is to append only external
* properties that do not already exist in the destination. The flag
* doAllProperties
can be used to operate on all properties, external and internal.
* The flag replaceOldValues
option can be used to replace the values
* of existing properties. The notion of external
* versus internal applies only to top level properties. The keep-or-replace-old notion applies
* within structs and arrays as described below.
*
* - If
replaceOldValues
is true then the processing is restricted to the top
* level properties. The processed properties from the source (according to
* doAllProperties
) are propagated to the destination,
* replacing any existing values.Properties in the destination that are not in the source
* are left alone.
*
* - If
replaceOldValues
is not passed then the processing is more complicated.
* Top level properties are added to the destination if they do not already exist.
* If they do exist but differ in form (simple/struct/array) then the destination is left alone.
* If the forms match, simple properties are left unchanged while structs and arrays are merged.
*
* - If
deleteEmptyValues
is passed then an empty value in the source XMP causes
* the corresponding destination XMP property to be deleted. The default is to treat empty
* values the same as non-empty values. An empty value is any of a simple empty string, an array
* with no items, or a struct with no fields. Qualifiers are ignored.
*
*
* The detailed behavior is defined by the following pseudo-code:
*
*
* appendProperties ( sourceXMP, destXMP, doAllProperties,
* replaceOldValues, deleteEmptyValues ):
* for all source schema (top level namespaces):
* for all top level properties in sourceSchema:
* if doAllProperties or prop is external:
* appendSubtree ( sourceNode, destSchema, replaceOldValues, deleteEmptyValues )
*
* appendSubtree ( sourceNode, destParent, replaceOldValues, deleteEmptyValues ):
* if deleteEmptyValues and source value is empty:
* delete the corresponding child from destParent
* else if sourceNode not in destParent (by name):
* copy sourceNode's subtree to destParent
* else if replaceOld:
* delete subtree from destParent
* copy sourceNode's subtree to destParent
* else:
* // Already exists in dest and not replacing, merge structs and arrays
* if sourceNode and destNode forms differ:
* return, leave the destNode alone
* else if form is a struct:
* for each field in sourceNode:
* AppendSubtree ( sourceNode.field, destNode, replaceOldValues )
* else if form is an alt-text array:
* copy new items by "xml:lang" value into the destination
* else if form is an array:
* copy new items by value into the destination, ignoring order and duplicates
*
*
*
* Note: appendProperties can be expensive if replaceOldValues is not passed and
* the XMP contains large arrays. The array item checking described above is n-squared.
* Each source item is checked to see if it already exists in the destination,
* without regard to order or duplicates.
*
Simple items are compared by value and "xml:lang" qualifier, other qualifiers are ignored.
* Structs are recursively compared by field names, without regard to field order. Arrays are
* compared by recursively comparing all items.
*
* @param source The source XMP object.
* @param dest The destination XMP object.
* @param doAllProperties Do internal properties in addition to external properties.
* @param replaceOldValues Replace the values of existing properties.
* @param deleteEmptyValues Delete destination values if source property is empty.
* @throws XMPException Forwards the Exceptions from the metadata processing
*/
public static void appendProperties(XMPMeta source, XMPMeta dest, boolean doAllProperties,
boolean replaceOldValues, boolean deleteEmptyValues) throws XMPException
{
XMPUtilsImpl.appendProperties(source, dest, doAllProperties, replaceOldValues,
deleteEmptyValues);
}
/**
* Convert from string to Boolean.
*
* @param value
* The string representation of the Boolean.
* @return The appropriate boolean value for the string. The checked values
* for true
and false
are:
*
* - {@link XMPConst#TRUESTR} and {@link XMPConst#FALSESTR}
*
- "t" and "f"
*
- "on" and "off"
*
- "yes" and "no"
*
- "value != 0" and "value == 0"
*
* @throws XMPException If an empty string is passed.
*/
public static boolean convertToBoolean(String value) throws XMPException
{
if (value == null || value.length() == 0)
{
throw new XMPException("Empty convert-string", XMPError.BADVALUE);
}
value = value.toLowerCase();
try
{
// First try interpretation as Integer (anything not 0 is true)
return Integer.parseInt(value) != 0;
}
catch (NumberFormatException e)
{
return
"true".equals(value) ||
"t".equals(value) ||
"on".equals(value) ||
"yes".equals(value);
}
}
/**
* Convert from boolean to string.
*
* @param value
* a boolean value
* @return The XMP string representation of the boolean. The values used are
* given by the constnts {@link XMPConst#TRUESTR} and
* {@link XMPConst#FALSESTR}.
*/
public static String convertFromBoolean(boolean value)
{
return value ? XMPConst.TRUESTR : XMPConst.FALSESTR;
}
/**
* Converts a string value to an int
.
*
* @param rawValue
* the string value
* @return Returns an int.
* @throws XMPException
* If the rawValue
is null
or empty or the
* conversion fails.
*/
public static int convertToInteger(String rawValue) throws XMPException
{
try
{
if (rawValue == null || rawValue.length() == 0)
{
throw new XMPException("Empty convert-string", XMPError.BADVALUE);
}
if (rawValue.startsWith("0x"))
{
return Integer.parseInt(rawValue.substring(2), 16);
}
else
{
return Integer.parseInt(rawValue);
}
}
catch (NumberFormatException e)
{
throw new XMPException("Invalid integer string", XMPError.BADVALUE);
}
}
/**
* Convert from int to string.
*
* @param value
* an int value
* @return The string representation of the int.
*/
public static String convertFromInteger(int value)
{
return String.valueOf(value);
}
/**
* Converts a string value to a long
.
*
* @param rawValue
* the string value
* @return Returns a long.
* @throws XMPException
* If the rawValue
is null
or empty or the
* conversion fails.
*/
public static long convertToLong(String rawValue) throws XMPException
{
try
{
if (rawValue == null || rawValue.length() == 0)
{
throw new XMPException("Empty convert-string", XMPError.BADVALUE);
}
if (rawValue.startsWith("0x"))
{
return Long.parseLong(rawValue.substring(2), 16);
}
else
{
return Long.parseLong(rawValue);
}
}
catch (NumberFormatException e)
{
throw new XMPException("Invalid long string", XMPError.BADVALUE);
}
}
/**
* Convert from long to string.
*
* @param value
* a long value
* @return The string representation of the long.
*/
public static String convertFromLong(long value)
{
return String.valueOf(value);
}
/**
* Converts a string value to a double
.
*
* @param rawValue
* the string value
* @return Returns a double.
* @throws XMPException
* If the rawValue
is null
or empty or the
* conversion fails.
*/
public static double convertToDouble(String rawValue) throws XMPException
{
try
{
if (rawValue == null || rawValue.length() == 0)
{
throw new XMPException("Empty convert-string", XMPError.BADVALUE);
}
else
{
return Double.parseDouble(rawValue);
}
}
catch (NumberFormatException e)
{
throw new XMPException("Invalid double string", XMPError.BADVALUE);
}
}
/**
* Convert from long to string.
*
* @param value
* a long value
* @return The string representation of the long.
*/
public static String convertFromDouble(double value)
{
return String.valueOf(value);
}
/**
* Converts a string value to an XMPDateTime
.
*
* @param rawValue
* the string value
* @return Returns an XMPDateTime
-object.
* @throws XMPException
* If the rawValue
is null
or empty or the
* conversion fails.
*/
public static XMPDateTime convertToDate(String rawValue) throws XMPException
{
if (rawValue == null || rawValue.length() == 0)
{
throw new XMPException("Empty convert-string", XMPError.BADVALUE);
}
else
{
return ISO8601Converter.parse(rawValue);
}
}
/**
* Convert from XMPDateTime
to string.
*
* @param value
* an XMPDateTime
* @return The string representation of the long.
*/
public static String convertFromDate(XMPDateTime value)
{
return ISO8601Converter.render(value);
}
/**
* Convert from a byte array to a base64 encoded string.
*
* @param buffer
* the byte array to be converted
* @return Returns the base64 string.
*/
public static String encodeBase64(byte[] buffer)
{
return new String(Base64.encode(buffer));
}
/**
* Decode from Base64 encoded string to raw data.
*
* @param base64String
* a base64 encoded string
* @return Returns a byte array containg the decoded string.
* @throws XMPException Thrown if the given string is not property base64 encoded
*/
public static byte[] decodeBase64(String base64String) throws XMPException
{
try
{
return Base64.decode(base64String.getBytes());
}
catch (Throwable e)
{
throw new XMPException("Invalid base64 string", XMPError.BADVALUE, e);
}
}
/**
* creates XMP serializations appropriate for a JPEG file.
*
* The standard XMP in a JPEG file is limited to 64K bytes. This function
* serializes the XMP metadata in an XMP object into a string of RDF . If
* the data does not fit into the 64K byte limit, it creates a second packet
* string with the extended data.
*
* @param origXMP
* The XMP object containing the metadata.
*
* @param stdStr
* A string builder object in which to return the full standard XMP
* packet.
*
* @param extStr
* A string builder object in which to return the serialized extended
* XMP, empty if not needed.
*
* @param digestStr
* A string builder object in which to return an MD5 digest of the
* serialized extended XMP, empty if not needed.
*
* @throws NoSuchAlgorithmException if fail to find algorithm for MD5
* @throws XMPException in case of internal error occurs.
*
*/
public static void packageForJPEG(XMPMeta origXMP,
StringBuilder stdStr,
StringBuilder extStr,
StringBuilder digestStr) throws NoSuchAlgorithmException, XMPException{
XMPUtilsImpl.packageForJPEG(origXMP,stdStr,extStr,digestStr);
}
/**
* merges standard and extended XMP retrieved from a JPEG file.
*
* When an extended partition stores properties that do not fit into the
* JPEG file limitation of 64K bytes, this function integrates those
* properties back into the same XMP object with those from the standard XMP
* packet.
*
* @param fullXMP
* An XMP object which the caller has initialized from the
* standard XMP packet in a JPEG file. The extended XMP is added
* to this object.
*
* @param extendedXMP
* An XMP object which the caller has initialized from the
* extended XMP packet in a JPEG file.
*
* @throws XMPException
* in case of internal error occurs.
*/
public static void mergeFromJPEG(XMPMeta fullXMP,
XMPMeta extendedXMP) throws XMPException{
XMPUtilsImpl.mergeFromJPEG(fullXMP,extendedXMP);
}
/**
* modifies a working XMP object according to a template object.
*
* The XMP template can be used to add, replace or delete properties from
* the working XMP object. The actions that you specify determine how the
* template is applied. Each action can be applied individually or combined;
* if you do not specify any actions, the properties and values in the
* working XMP object do not change.
*
* These actions are available:
*
* - Clear
CLEAR_UNNAMED_PROPERTIES
: Deletes top-level
* properties. Any top-level property that is present in the template (even
* with empty value) is retained. All other top-level properties in the
* working object are deleted
*
* - Add
ADD_NEW_PROPERTIES
: Adds new properties to the
* working object if the template properties have values. See additional
* detail below.
*
* - Replace
REPLACE_EXISTING_PROPERTIES
: Replaces the
* values of existing top-level properties in the working XMP if the value
* forms match those in the template. Properties with empty values in the
* template are ignored. If combined with Clear or Add actions, those take
* precedence; values are cleared or added, rather than replaced.
*
* - Replace/Delete empty
REPLACE_WITH_DELETE_EMPTY
:
* Replaces values in the same way as the simple Replace action, and also
* deletes properties if the value in the template is empty. If combined
* with Clear or Add actions, those take precedence; values are cleared or
* added, rather than replaced.
*
* - Include internal
INCLUDE_INTERNAL_PROPERTIES
: Performs
* specified action on internal properties as well as external properties.
* By default, internal properties are ignored for all actions.
*
*
* The Add behavior depends on the type of property:
*
* - If a top-level property is not in the working XMP, and has a value in
* the template, the property and value are added. Empty properties are not
* added.
*
- If a property is in both the working XMP and template, the value
* forms must match, otherwise the template is ignored for that property.
*
- If a struct is present in both the working XMP and template, the
* individual fields of the template struct are added as appropriate; that
* is, the logic is recursively applied to the fields. Struct values are
* equivalent if they have the same fields with equivalent values.
*
- If an array is present in both the working XMP and template, items
* from the template are added if the value forms match. Array values match
* if they have sets of equivalent items, regardless of order.
*
- Alt-text arrays use the \c xml:lang qualifier as a key, adding
* languages that are missing.
*
* Array item checking is n-squared; this can be time-intensive if the
* Replace option is not specified. Each source item is checked to see if it
* already exists in the destination, without regard to order or duplicates.
* Simple items are compared by value and xml:lang
qualifier;
* other qualifiers are ignored. Structs are recursively compared by field
* names, without regard to field order. Arrays are compared by recursively
* comparing all items.
*
* @param workingXMP
* The destination XMP object.
*
* @param templateXMP
* The template to apply to the destination XMP object.
*
* @param options
* Option flags to control the copying. If none are specified,
* the properties and values in the working XMP do not change. A
* logical OR of these bit-flag constants:
*
* CLEAR_UNNAMED_PROPERTIES
Delete anything
* that is not in the template
* ADD_NEW_PROPERTIES
Add properties; see
* detailed description.
* REPLACE_EXISTING_PROPERTIES
Replace the
* values of existing properties.
* REPLACE_WITH_DELETE_EMPTY
Replace the
* values of existing properties and delete properties if the new
* value is empty.
* INCLUDE_INTERNAL_PROPERTIES
Operate on
* internal properties as well as external properties.
*
*
* @throws XMPException
* in case of internal error occurs.
*/
static public void applyTemplate ( XMPMeta workingXMP,
XMPMeta templateXMP, TemplateOptions options)
throws XMPException{
XMPUtilsImpl.applyTemplate(workingXMP,templateXMP,options);
}
/**
*
* Replicate a subtree from one XMP object into another, possibly at a
* different location.
*
*
* @param source
* The source XMP object.
*
* @param dest
* The destination XMP object.
*
* @param sourceNS
* The schema namespace URI for the source subtree.
*
* @param sourceRoot
* The root location for the source subtree. May be a general
* path expression, must not be null or the empty string.
*
* @param destNS
* The schema namespace URI for the destination. Defaults to the
* source namespace.
*
* @param destRoot
* The root location for the destination. May be a general path
* expression. Defaults to the source location.
*
* @param options
* Option flags to control the separation. (For now, this argument is ignored.
* 0 should be passed.
* @throws XMPException throws an XMPException
*/
public static void duplicateSubtree(XMPMeta source, XMPMeta dest, String sourceNS,
String sourceRoot, String destNS, String destRoot, PropertyOptions options) throws XMPException
{
XMPUtilsImpl.duplicateSubtree(source, dest, sourceNS, sourceRoot, destNS, destRoot, options);
}
}