Home | History | Annotate | Download | only in xmp
      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;
     11 
     12 import com.adobe.xmp.impl.Utils;
     13 import com.adobe.xmp.impl.xpath.XMPPath;
     14 import com.adobe.xmp.impl.xpath.XMPPathParser;
     15 
     16 /**
     17  * Utility services for the metadata object. It has only public static functions, you cannot create
     18  * an object. These are all functions that layer cleanly on top of the core XMP toolkit.
     19  * <p>
     20  * These functions provide support for composing path expressions to deeply nested properties. The
     21  * functions <code>XMPMeta</code> such as <code>getProperty()</code>,
     22  * <code>getArrayItem()</code> and <code>getStructField()</code> provide easy access to top
     23  * level simple properties, items in top level arrays, and fields of top level structs. They do not
     24  * provide convenient access to more complex things like fields several levels deep in a complex
     25  * struct, or fields within an array of structs, or items of an array that is a field of a struct.
     26  * These functions can also be used to compose paths to top level array items or struct fields so
     27  * that you can use the binary accessors like <code>getPropertyAsInteger()</code>.
     28  * <p>
     29  * You can use these functions is to compose a complete path expression, or all but the last
     30  * component. Suppose you have a property that is an array of integers within a struct. You can
     31  * access one of the array items like this:
     32  * <p>
     33  * <blockquote>
     34  *
     35  * <pre>
     36  *      String path = XMPPathFactory.composeStructFieldPath (schemaNS, &quot;Struct&quot;, fieldNS,
     37  *          &quot;Array&quot;);
     38  *      String path += XMPPathFactory.composeArrayItemPath (schemaNS, &quot;Array&quot; index);
     39  *      PropertyInteger result = xmpObj.getPropertyAsInteger(schemaNS, path);
     40  * </pre>
     41  *
     42  * </blockquote> You could also use this code if you want the string form of the integer:
     43  * <blockquote>
     44  *
     45  * <pre>
     46  *      String path = XMPPathFactory.composeStructFieldPath (schemaNS, &quot;Struct&quot;, fieldNS,
     47  *          &quot;Array&quot;);
     48  *      PropertyText xmpObj.getArrayItem (schemaNS, path, index);
     49  * </pre>
     50  *
     51  * </blockquote>
     52  * <p>
     53  * <em>Note:</em> It might look confusing that the schemaNS is passed in all of the calls above.
     54  * This is because the XMP toolkit keeps the top level &quot;schema&quot; namespace separate from
     55  * the rest of the path expression.
     56  * <em>Note:</em> These methods are much simpler than in the C++-API, they don't check the given
     57  * path or array indices.
     58  *
     59  * @since 25.01.2006
     60  */
     61 public final class XMPPathFactory
     62 {
     63 	/** Private constructor */
     64 	private XMPPathFactory()
     65 	{
     66 		// EMPTY
     67 	}
     68 
     69 
     70 	/**
     71 	 * Compose the path expression for an item in an array.
     72 	 *
     73 	 * @param arrayName The name of the array. May be a general path expression, must not be
     74 	 *        <code>null</code> or the empty string.
     75 	 * @param itemIndex The index of the desired item. Arrays in XMP are indexed from 1.
     76 	 * 		  0 and below means last array item and renders as <code>[last()]</code>.
     77 	 *
     78 	 * @return Returns the composed path basing on fullPath. This will be of the form
     79 	 *         <tt>ns:arrayName[i]</tt>, where &quot;ns&quot; is the prefix for schemaNS and
     80 	 *         &quot;i&quot; is the decimal representation of itemIndex.
     81 	 * @throws XMPException Throws exeption if index zero is used.
     82 	 */
     83 	public static String composeArrayItemPath(String arrayName, int itemIndex) throws XMPException
     84 	{
     85 		if (itemIndex > 0)
     86 		{
     87 			return arrayName + '[' + itemIndex + ']';
     88 		}
     89 		else  if (itemIndex == XMPConst.ARRAY_LAST_ITEM)
     90 		{
     91 			return arrayName + "[last()]";
     92 		}
     93 		else
     94 		{
     95 			throw new XMPException("Array index must be larger than zero", XMPError.BADINDEX);
     96 		}
     97 	}
     98 
     99 
    100 	/**
    101 	 * Compose the path expression for a field in a struct. The result can be added to the
    102 	 * path of
    103 	 *
    104 	 *
    105 	 * @param fieldNS The namespace URI for the field. Must not be <code>null</code> or the empty
    106 	 *        string.
    107 	 * @param fieldName The name of the field. Must be a simple XML name, must not be
    108 	 *        <code>null</code> or the empty string.
    109 	 * @return Returns the composed path. This will be of the form
    110 	 *         <tt>ns:structName/fNS:fieldName</tt>, where &quot;ns&quot; is the prefix for
    111 	 *         schemaNS and &quot;fNS&quot; is the prefix for fieldNS.
    112 	 * @throws XMPException Thrown if the path to create is not valid.
    113 	 */
    114 	public static String composeStructFieldPath(String fieldNS,
    115 			String fieldName) throws XMPException
    116 	{
    117 		assertFieldNS(fieldNS);
    118 		assertFieldName(fieldName);
    119 
    120 		XMPPath fieldPath = XMPPathParser.expandXPath(fieldNS, fieldName);
    121 		if (fieldPath.size() != 2)
    122 		{
    123 			throw new XMPException("The field name must be simple", XMPError.BADXPATH);
    124 		}
    125 
    126 		return '/' + fieldPath.getSegment(XMPPath.STEP_ROOT_PROP).getName();
    127 	}
    128 
    129 
    130 	/**
    131 	 * Compose the path expression for a qualifier.
    132 	 *
    133 	 * @param qualNS The namespace URI for the qualifier. May be <code>null</code> or the empty
    134 	 *        string if the qualifier is in the XML empty namespace.
    135 	 * @param qualName The name of the qualifier. Must be a simple XML name, must not be
    136 	 *        <code>null</code> or the empty string.
    137 	 * @return Returns the composed path. This will be of the form
    138 	 *         <tt>ns:propName/?qNS:qualName</tt>, where &quot;ns&quot; is the prefix for
    139 	 *         schemaNS and &quot;qNS&quot; is the prefix for qualNS.
    140 	 * @throws XMPException Thrown if the path to create is not valid.
    141 	 */
    142 	public static String composeQualifierPath(
    143 			String qualNS,
    144 			String qualName) throws XMPException
    145 	{
    146 		assertQualNS(qualNS);
    147 		assertQualName(qualName);
    148 
    149 		XMPPath qualPath = XMPPathParser.expandXPath(qualNS, qualName);
    150 		if (qualPath.size() != 2)
    151 		{
    152 			throw new XMPException("The qualifier name must be simple", XMPError.BADXPATH);
    153 		}
    154 
    155 		return "/?" + qualPath.getSegment(XMPPath.STEP_ROOT_PROP).getName();
    156 	}
    157 
    158 
    159 	/**
    160 	 * Compose the path expression to select an alternate item by language. The
    161 	 * path syntax allows two forms of &quot;content addressing&quot; that may
    162 	 * be used to select an item in an array of alternatives. The form used in
    163 	 * ComposeLangSelector lets you select an item in an alt-text array based on
    164 	 * the value of its <tt>xml:lang</tt> qualifier. The other form of content
    165 	 * addressing is shown in ComposeFieldSelector. \note ComposeLangSelector
    166 	 * does not supplant SetLocalizedText or GetLocalizedText. They should
    167 	 * generally be used, as they provide extra logic to choose the appropriate
    168 	 * language and maintain consistency with the 'x-default' value.
    169 	 * ComposeLangSelector gives you an path expression that is explicitly and
    170 	 * only for the language given in the langName parameter.
    171 	 *
    172 	 * @param arrayName
    173 	 *            The name of the array. May be a general path expression, must
    174 	 *            not be <code>null</code> or the empty string.
    175 	 * @param langName
    176 	 *            The RFC 3066 code for the desired language.
    177 	 * @return Returns the composed path. This will be of the form
    178 	 *         <tt>ns:arrayName[@xml:lang='langName']</tt>, where
    179 	 *         &quot;ns&quot; is the prefix for schemaNS.
    180 	 */
    181 	public static String composeLangSelector(String arrayName,
    182 			String langName)
    183 	{
    184 		return arrayName + "[?xml:lang=\"" + Utils.normalizeLangValue(langName) + "\"]";
    185 	}
    186 
    187 
    188 	/**
    189 	 * Compose the path expression to select an alternate item by a field's value. The path syntax
    190 	 * allows two forms of &quot;content addressing&quot; that may be used to select an item in an
    191 	 * array of alternatives. The form used in ComposeFieldSelector lets you select an item in an
    192 	 * array of structs based on the value of one of the fields in the structs. The other form of
    193 	 * content addressing is shown in ComposeLangSelector. For example, consider a simple struct
    194 	 * that has two fields, the name of a city and the URI of an FTP site in that city. Use this to
    195 	 * create an array of download alternatives. You can show the user a popup built from the values
    196 	 * of the city fields. You can then get the corresponding URI as follows:
    197 	 * <p>
    198 	 * <blockquote>
    199 	 *
    200 	 * <pre>
    201 	 *      String path = composeFieldSelector ( schemaNS, &quot;Downloads&quot;, fieldNS,
    202 	 *          &quot;City&quot;, chosenCity );
    203 	 *      XMPProperty prop = xmpObj.getStructField ( schemaNS, path, fieldNS, &quot;URI&quot; );
    204 	 * </pre>
    205 	 *
    206 	 * </blockquote>
    207 	 *
    208 	 * @param arrayName The name of the array. May be a general path expression, must not be
    209 	 *        <code>null</code> or the empty string.
    210 	 * @param fieldNS The namespace URI for the field used as the selector. Must not be
    211 	 *        <code>null</code> or the empty string.
    212 	 * @param fieldName The name of the field used as the selector. Must be a simple XML name, must
    213 	 *        not be <code>null</code> or the empty string. It must be the name of a field that is
    214 	 *        itself simple.
    215 	 * @param fieldValue The desired value of the field.
    216 	 * @return Returns the composed path. This will be of the form
    217 	 *         <tt>ns:arrayName[fNS:fieldName='fieldValue']</tt>, where &quot;ns&quot; is the
    218 	 *         prefix for schemaNS and &quot;fNS&quot; is the prefix for fieldNS.
    219 	 * @throws XMPException Thrown if the path to create is not valid.
    220 	 */
    221 	public static String composeFieldSelector(String arrayName, String fieldNS,
    222 			String fieldName, String fieldValue) throws XMPException
    223 	{
    224 		XMPPath fieldPath = XMPPathParser.expandXPath(fieldNS, fieldName);
    225 		if (fieldPath.size() != 2)
    226 		{
    227 			throw new XMPException("The fieldName name must be simple", XMPError.BADXPATH);
    228 		}
    229 
    230 		return arrayName + '[' + fieldPath.getSegment(XMPPath.STEP_ROOT_PROP).getName() +
    231 			"=\"" + fieldValue + "\"]";
    232 	}
    233 
    234 
    235 	/**
    236 	 * ParameterAsserts that a qualifier namespace is set.
    237 	 * @param qualNS a qualifier namespace
    238 	 * @throws XMPException Qualifier schema is null or empty
    239 	 */
    240 	private static void assertQualNS(String qualNS) throws XMPException
    241 	{
    242 		if (qualNS == null  ||  qualNS.length() == 0)
    243 		{
    244 			throw new XMPException("Empty qualifier namespace URI", XMPError.BADSCHEMA);
    245 		}
    246 
    247 	}
    248 
    249 
    250 	/**
    251 	 * ParameterAsserts that a qualifier name is set.
    252 	 * @param qualName a qualifier name or path
    253 	 * @throws XMPException Qualifier name is null or empty
    254 	 */
    255 	private static void assertQualName(String qualName) throws XMPException
    256 	{
    257 		if (qualName == null  ||  qualName.length() == 0)
    258 		{
    259 			throw new XMPException("Empty qualifier name", XMPError.BADXPATH);
    260 		}
    261 	}
    262 
    263 
    264 	/**
    265 	 * ParameterAsserts that a struct field namespace is set.
    266 	 * @param fieldNS a struct field namespace
    267 	 * @throws XMPException Struct field schema is null or empty
    268 	 */
    269 	private static void assertFieldNS(String fieldNS) throws XMPException
    270 	{
    271 		if (fieldNS == null  ||  fieldNS.length() == 0)
    272 		{
    273 			throw new XMPException("Empty field namespace URI", XMPError.BADSCHEMA);
    274 		}
    275 
    276 	}
    277 
    278 
    279 	/**
    280 	 * ParameterAsserts that a struct field name is set.
    281 	 * @param fieldName a struct field name or path
    282 	 * @throws XMPException Struct field name is null or empty
    283 	 */
    284 	private static void assertFieldName(String fieldName) throws XMPException
    285 	{
    286 		if (fieldName == null  ||  fieldName.length() == 0)
    287 		{
    288 			throw new XMPException("Empty f name", XMPError.BADXPATH);
    289 		}
    290 	}
    291 }