All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.ashampoo.xmp.XMPPathFactory.kt Maven / Gradle / Ivy

// =================================================================================================
// 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.ashampoo.xmp

import com.ashampoo.xmp.internal.Utils
import com.ashampoo.xmp.internal.XMPErrorConst
import com.ashampoo.xmp.internal.XMPPath
import com.ashampoo.xmp.internal.XMPPathParser

/**
 * Utility services for the metadata object. It has only public static functions, you cannot create
 * an object. These are all functions that layer cleanly on top of the core XMP toolkit.
 *
 * These functions provide support for composing path expressions to deeply nested properties. The
 * functions `XMPMeta` such as `getProperty()`,
 * `getArrayItem()` and `getStructField()` provide easy access to top
 * level simple properties, items in top level arrays, and fields of top level structs. They do not
 * provide convenient access to more complex things like fields several levels deep in a complex
 * struct, or fields within an array of structs, or items of an array that is a field of a struct.
 * These functions can also be used to compose paths to top level array items or struct fields so
 * that you can use the binary accessors like `getPropertyAsInteger()`.
 *
 * You can use these functions is to compose a complete path expression, or all but the last
 * component. Suppose you have a property that is an array of integers within a struct.
 *
 * *Note:* It might look confusing that the schemaNS is passed in all of the calls above.
 * This is because the XMP toolkit keeps the top level "schema" namespace separate from
 * the rest of the path expression.
 *
 * *Note:* These methods are much simpler than in the C++-API, they don't check the given path or array indices.
 */
public object XMPPathFactory {

    /**
     * Compose the path expression for an item in an array.
     *
     * @param arrayName The name of the array.
     *                  May be a general path expression, must not be `null` or the empty string.
     * @param itemIndex The index of the desired item. Arrays in XMP are indexed from 1.
     *                  0 and below means last array item and renders as `[last()]`.
     * @return Returns the composed path basing on fullPath. This will be of the form
     *         ns:arrayName[i], where "ns" is the prefix for schemaNS and
     *         "i" is the decimal representation of itemIndex.
     */
    @kotlin.jvm.JvmStatic
    public fun composeArrayItemPath(arrayName: String, itemIndex: Int): String {

        if (itemIndex > 0)
            return "$arrayName[$itemIndex]"

        if (itemIndex == XMPConst.ARRAY_LAST_ITEM)
            return "$arrayName[last()]"

        throw XMPException("Array index must be larger than zero", XMPErrorConst.BADINDEX)
    }

    /**
     * Compose the path expression for a field in a struct. The result can be added to the
     * path of
     *
     * @param fieldNS   The namespace URI for the field. Must not be `null` or the empty string.
     * @param fieldName The name of the field. Must be a simple XML name,
     *                  must not be `null` or the empty string.
     * @return Returns the composed path. This will be of the form
     *         ns:structName/fNS:fieldName, where "ns" is the prefix for
     *         schemaNS and "fNS" is the prefix for fieldNS.
     */
    @kotlin.jvm.JvmStatic
    public fun composeStructFieldPath(fieldNS: String, fieldName: String): String {

        if (fieldNS.isEmpty())
            throw XMPException("Empty field namespace URI", XMPErrorConst.BADSCHEMA)

        if (fieldName.isEmpty())
            throw XMPException("Empty field name", XMPErrorConst.BADXPATH)

        val fieldPath = XMPPathParser.expandXPath(fieldNS, fieldName)

        if (fieldPath.size() != 2)
            throw XMPException("The field name must be simple", XMPErrorConst.BADXPATH)

        return '/'.toString() + fieldPath.getSegment(XMPPath.STEP_ROOT_PROP).name
    }

    /**
     * Compose the path expression for a qualifier.
     *
     * @param qualNS   The namespace URI for the qualifier.
     *                 May be `null` or the empty string if the qualifier is in the XML empty namespace.
     * @param qualName The name of the qualifier.
     *                 Must be a simple XML name, must not be `null` or the empty string.
     * @return Returns the composed path. This will be of the form
     *         ns:propName/?qNS:qualName, where "ns" is the prefix for
     *         schemaNS and "qNS" is the prefix for qualNS.
     */
    @kotlin.jvm.JvmStatic
    public fun composeQualifierPath(qualNS: String, qualName: String): String {

        if (qualNS.isEmpty())
            throw XMPException("Empty qualifier namespace URI", XMPErrorConst.BADSCHEMA)

        if (qualName.isEmpty())
            throw XMPException("Empty qualifier name", XMPErrorConst.BADXPATH)

        val qualPath = XMPPathParser.expandXPath(qualNS, qualName)

        if (qualPath.size() != 2)
            throw XMPException("The qualifier name must be simple", XMPErrorConst.BADXPATH)

        return "/?" + qualPath.getSegment(XMPPath.STEP_ROOT_PROP).name
    }

    /**
     * Compose the path expression to select an alternate item by language. The
     * path syntax allows two forms of "content addressing" that may
     * be used to select an item in an array of alternatives. The form used in
     * ComposeLangSelector lets you select an item in an alt-text array based on
     * the value of its xml:lang qualifier. The other form of content
     * addressing is shown in ComposeFieldSelector.
     *
     * ComposeLangSelector does not supplant SetLocalizedText or GetLocalizedText.
     * They should generally be used, as they provide extra logic to choose the appropriate
     * language and maintain consistency with the 'x-default' value.
     * ComposeLangSelector gives you an path expression that is explicitly and
     * only for the language given in the langName parameter.
     *
     * @param arrayName The name of the array.
     *                  May be a general path expression, must not be `null` or the empty string.
     * @param langName  The RFC 3066 code for the desired language.
     * @return Returns the composed path. This will be of the form
     *         ns:arrayName[@xml:lang='langName'], where
     *         "ns" is the prefix for schemaNS.
     */
    public fun composeLangSelector(arrayName: String, langName: String): String =
        arrayName + "[?xml:lang=\"" + Utils.normalizeLangValue(langName) + "\"]"

    /**
     * Compose the path expression to select an alternate item by a field's value. The path syntax
     * allows two forms of "content addressing" that may be used to select an item in an
     * array of alternatives. The form used in ComposeFieldSelector lets you select an item in an
     * array of structs based on the value of one of the fields in the structs. The other form of
     * content addressing is shown in ComposeLangSelector. For example, consider a simple struct
     * that has two fields, the name of a city and the URI of an FTP site in that city. Use this to
     * create an array of download alternatives. You can show the user a popup built from the values
     * of the city fields.
     *
     * @param arrayName  The name of the array.
     *                   May be a general path expression, must not be `null` or the empty string.
     * @param fieldNS    The namespace URI for the field used as the selector.
     *                   Must not be `null` or the empty string.
     * @param fieldName  The name of the field used as the selector.
     *                   Must be a simple XML name, must not be `null` or the empty string.
     *                   It must be the name of a field that is itself simple.
     * @param fieldValue The desired value of the field.
     * @return Returns the composed path. This will be of the form
     *         ns:arrayName[fNS:fieldName='fieldValue'], where "ns" is the
     *         prefix for schemaNS and "fNS" is the prefix for fieldNS.
     */
    public fun composeFieldSelector(
        arrayName: String,
        fieldNS: String?,
        fieldName: String?,
        fieldValue: String
    ): String {

        val fieldPath = XMPPathParser.expandXPath(fieldNS, fieldName)

        if (fieldPath.size() != 2)
            throw XMPException("The fieldName name must be simple", XMPErrorConst.BADXPATH)

        return arrayName +
            '[' + fieldPath.getSegment(XMPPath.STEP_ROOT_PROP).name + "=\"" + fieldValue + "\"]"
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy