Package com.adobe.xmp.impl

Source Code of com.adobe.xmp.impl.XMPNodeUtils

// =================================================================================================
// 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.GregorianCalendar;
import java.util.Iterator;

import com.adobe.xmp.XMPConst;
import com.adobe.xmp.XMPDateTime;
import com.adobe.xmp.XMPDateTimeFactory;
import com.adobe.xmp.XMPError;
import com.adobe.xmp.XMPException;
import com.adobe.xmp.XMPMetaFactory;
import com.adobe.xmp.XMPUtils;
import com.adobe.xmp.impl.xpath.XMPPath;
import com.adobe.xmp.impl.xpath.XMPPathSegment;
import com.adobe.xmp.options.AliasOptions;
import com.adobe.xmp.options.PropertyOptions;


/**
* Utilities for <code>XMPNode</code>.
*
* @since   Aug 28, 2006
*/
public class XMPNodeUtils implements XMPConst
{
  /** */
  static final int CLT_NO_VALUES = 0;
  /** */
  static final int CLT_SPECIFIC_MATCH = 1;
  /** */
  static final int CLT_SINGLE_GENERIC = 2;
  /** */
  static final int CLT_MULTIPLE_GENERIC = 3;
  /** */
  static final int CLT_XDEFAULT = 4;
  /** */
  static final int CLT_FIRST_ITEM = 5;


  /**
   * Private Constructor
   */
  private XMPNodeUtils()
  {
    // EMPTY
  }

 
  /**
   * Find or create a schema node if <code>createNodes</code> is false and
   * 
   * @param tree the root of the xmp tree.
   * @param namespaceURI a namespace
   * @param createNodes a flag indicating if the node shall be created if not found.
   *       <em>Note:</em> The namespace must be registered prior to this call.
   *
   * @return Returns the schema node if found, <code>null</code> otherwise.
   *        Note: If <code>createNodes</code> is <code>true</code>, it is <b>always</b>
   *        returned a valid node.
   * @throws XMPException An exception is only thrown if an error occurred, not if a
   *             node was not found.
   */
  static XMPNode findSchemaNode(XMPNode tree, String namespaceURI,
      boolean createNodes)
      throws XMPException
  {
    return findSchemaNode(tree, namespaceURI, null, createNodes);
  }
 
 
  /**
   * Find or create a schema node if <code>createNodes</code> is true.
   * 
   * @param tree the root of the xmp tree.
   * @param namespaceURI a namespace
   * @param suggestedPrefix If a prefix is suggested, the namespace is allowed to be registered.
   * @param createNodes a flag indicating if the node shall be created if not found.
   *       <em>Note:</em> The namespace must be registered prior to this call.
   *
   * @return Returns the schema node if found, <code>null</code> otherwise.
   *        Note: If <code>createNodes</code> is <code>true</code>, it is <b>always</b>
   *        returned a valid node.
   * @throws XMPException An exception is only thrown if an error occurred, not if a
   *             node was not found.
   */
  static XMPNode findSchemaNode(XMPNode tree, String namespaceURI, String suggestedPrefix,
      boolean createNodes)
      throws XMPException
  {
    assert tree.getParent() == null; // make sure that its the root
    XMPNode schemaNode = tree.findChildByName(namespaceURI);
   
    if (schemaNode == null  &&  createNodes)
    {
      schemaNode = new XMPNode(namespaceURI,
        new PropertyOptions()
          .setSchemaNode(true));
      schemaNode.setImplicit(true);
     
      // only previously registered schema namespaces are allowed in the XMP tree.
      String prefix = XMPMetaFactory.getSchemaRegistry().getNamespacePrefix(namespaceURI);
      if (prefix == null)
      { 
        if (suggestedPrefix != null  &&  suggestedPrefix.length() != 0)
        {
          prefix = XMPMetaFactory.getSchemaRegistry().registerNamespace(namespaceURI,
              suggestedPrefix);
        }
        else
        {
          throw new XMPException("Unregistered schema namespace URI",
              XMPError.BADSCHEMA);
       
      } 
       
      schemaNode.setValue(prefix);
 
      tree.addChild(schemaNode);
    }
   
    return schemaNode;
  }

 
  /**
   * Find or create a child node under a given parent node. If the parent node is no
   * Returns the found or created child node.
   *
   * @param parent
   *            the parent node
   * @param childName
   *            the node name to find
   * @param createNodes
   *            flag, if new nodes shall be created.
   * @return Returns the found or created node or <code>null</code>.
   * @throws XMPException Thrown if
   */
  static XMPNode findChildNode(XMPNode parent, String childName, boolean createNodes)
      throws XMPException
  {
    if (!parent.getOptions().isSchemaNode() && !parent.getOptions().isStruct())
    {
      if (!parent.isImplicit())
      {
        throw new XMPException("Named children only allowed for schemas and structs",
            XMPError.BADXPATH);
     
      else if (parent.getOptions().isArray())
      {
        throw new XMPException("Named children not allowed for arrays",
            XMPError.BADXPATH);
      }
      else if (createNodes)
      { 
        parent.getOptions().setStruct(true);
     
    }
     
    XMPNode childNode = parent.findChildByName(childName);
   
    if (childNode == null  &&  createNodes)
    {
      PropertyOptions options = new PropertyOptions();
      childNode = new XMPNode(childName, options);
      childNode.setImplicit(true);
      parent.addChild(childNode);
    }
   
    assert childNode != null ||  !createNodes;
 
    return childNode;
  }


  /**
   * Follow an expanded path expression to find or create a node.
   *
   * @param xmpTree the node to begin the search.
   * @param xpath the complete xpath
   * @param createNodes flag if nodes shall be created
   *       (when called by <code>setProperty()</code>)
   * @param leafOptions the options for the created leaf nodes (only when
   *      <code>createNodes == true</code>).
   * @return Returns the node if found or created or <code>null</code>.
   * @throws XMPException An exception is only thrown if an error occurred,
   *       not if a node was not found.
   */
  static XMPNode findNode(XMPNode xmpTree, XMPPath xpath, boolean createNodes,
    PropertyOptions leafOptions) throws XMPException
  {
    // check if xpath is set.
    if (xpath == null  ||  xpath.size() == 0)
    {
      throw new XMPException("Empty XMPPath", XMPError.BADXPATH);
    }

    // Root of implicitly created subtree to possible delete it later.
    // Valid only if leaf is new.
    XMPNode rootImplicitNode = null;
    XMPNode currNode = null;
   
    // resolve schema step
    currNode = findSchemaNode(xmpTree,
      xpath.getSegment(XMPPath.STEP_SCHEMA).getName(), createNodes);
    if (currNode == null)
    {
      return null;
    }
    else if (currNode.isImplicit())
    {
      currNode.setImplicit(false)// Clear the implicit node bit.
      rootImplicitNode = currNode;  // Save the top most implicit node.
    }


    // Now follow the remaining steps of the original XMPPath.
    try
    {
      for (int i = 1; i < xpath.size(); i++)
      {
        currNode = followXPathStep(currNode, xpath.getSegment(i), createNodes);
        if (currNode == null)
        {
          if (createNodes)
          { 
            // delete implicitly created nodes
            deleteNode(rootImplicitNode);
         
          return null;
        }
        else if (currNode.isImplicit())
        {
          // clear the implicit node flag
          currNode.setImplicit(false);

          // if node is an ALIAS (can be only in root step, auto-create array
          // when the path has been resolved from a not simple alias type
          if (i == && 
            xpath.getSegment(i).isAlias()  &&
            xpath.getSegment(i).getAliasForm() != 0)
          {
            currNode.getOptions().setOption(xpath.getSegment(i).getAliasForm(), true);
          }
          // "CheckImplicitStruct" in C++
          else if (i < xpath.size() - &&
            xpath.getSegment(i).getKind() == XMPPath.STRUCT_FIELD_STEP  && 
            !currNode.getOptions().isCompositeProperty())
          {
            currNode.getOptions().setStruct(true);
          }         
         
          if (rootImplicitNode == null)
          {
            rootImplicitNode = currNode;  // Save the top most implicit node.
          }
        }
      }
    }
    catch (XMPException e)
    {
      // if new notes have been created prior to the error, delete them
      if (rootImplicitNode != null)
      {
        deleteNode(rootImplicitNode);
      }
      throw e;
    }
   
   
    if (rootImplicitNode != null)
    {
      // set options only if a node has been successful created
      currNode.getOptions().mergeWith(leafOptions);
      currNode.setOptions(currNode.getOptions());
    }
   
    return currNode;
  }


  /**
   * Deletes the the given node and its children from its parent.
   * Takes care about adjusting the flags.
   * @param node the top-most node to delete.
   */
  static void deleteNode(XMPNode node)
  {
    XMPNode parent = node.getParent();
   
    if (node.getOptions().isQualifier())
    {
      // root is qualifier
      parent.removeQualifier(node);
    }
    else
    {
      // root is NO qualifier
      parent.removeChild(node);
    }
   
    // delete empty Schema nodes
    if (!parent.hasChildren()  &&  parent.getOptions().isSchemaNode())
    {
      parent.getParent().removeChild(parent);
    }
  }


  /**
   * This is setting the value of a leaf node.
   *
   * @param node an XMPNode
   * @param value a value
   */
  static void setNodeValue(XMPNode node, Object value)
  {
    String strValue = serializeNodeValue(value);
    if (!(node.getOptions().isQualifier()  &&  XML_LANG.equals(node.getName())))
    { 
      node.setValue(strValue);
    }
    else
    {
      node.setValue(Utils.normalizeLangValue(strValue));
    }
  }
 
 
  /**
   * Verifies the PropertyOptions for consistancy and updates them as needed.
   * If options are <code>null</code> they are created with default values.
   * 
   * @param options the <code>PropertyOptions</code>
   * @param itemValue the node value to set
   * @return Returns the updated options.
   * @throws XMPException If the options are not consistant.
   */
  static PropertyOptions verifySetOptions(PropertyOptions options, Object itemValue)
      throws XMPException
  {
    // create empty and fix existing options
    if (options == null)
    {
      // set default options
      options = new PropertyOptions();
    }
   
    if (options.isArrayAltText())
    {
      options.setArrayAlternate(true);
    }
 
    if (options.isArrayAlternate())
    {
      options.setArrayOrdered(true);
    }
 
    if (options.isArrayOrdered())
    {
      options.setArray(true);
    }
 
    if (options.isCompositeProperty() && itemValue != null && itemValue.toString().length() > 0)
    {
      throw new XMPException("Structs and arrays can't have values",
        XMPError.BADOPTIONS);
    }
 
    options.assertConsistency(options.getOptions());
   
    return options;
  }


  /**
   * Converts the node value to String, apply special conversions for defined
   * types in XMP.
   *
   * @param value
   *            the node value to set
   * @return Returns the String representation of the node value.
   */
  static String serializeNodeValue(Object value)
  {
    String strValue;
    if (value == null)
    {
      strValue = null;
    }
    else if (value instanceof Boolean)
    {
      strValue = XMPUtils.convertFromBoolean(((Boolean) value).booleanValue());
    }
    else if (value instanceof Integer)
    {
      strValue = XMPUtils.convertFromInteger(((Integer) value).intValue());
    }
    else if (value instanceof Long)
    {
      strValue = XMPUtils.convertFromLong(((Long) value).longValue());
    }
    else if (value instanceof Double)
    {
      strValue = XMPUtils.convertFromDouble(((Double) value).doubleValue());
    }
    else if (value instanceof XMPDateTime)
    {
      strValue = XMPUtils.convertFromDate((XMPDateTime) value);
    }
    else if (value instanceof GregorianCalendar)
    {
      XMPDateTime dt = XMPDateTimeFactory.createFromCalendar((GregorianCalendar) value);
      strValue = XMPUtils.convertFromDate(dt);
    }
    else if (value instanceof byte[])
    {
      strValue = XMPUtils.encodeBase64((byte[]) value);
    }
    else
    {
      strValue = value.toString();
    }
 
    return strValue != null ? Utils.removeControlChars(strValue) : null;
  }
 
 
  /**
   * After processing by ExpandXPath, a step can be of these forms:
   * <ul>
   *   <li>qualName - A top level property or struct field.
   * <li>[index] - An element of an array.
   * <li>[last()] - The last element of an array.
   * <li>[qualName="value"] - An element in an array of structs, chosen by a field value.
   * <li>[?qualName="value"] - An element in an array, chosen by a qualifier value.
   * <li>?qualName - A general qualifier.
   * </ul>
   * Find the appropriate child node, resolving aliases, and optionally creating nodes.
   *
   * @param parentNode the node to start to start from
   * @param nextStep the xpath segment
   * @param createNodes
   * @return returns the found or created XMPPath node
   * @throws XMPException
   */
  private static XMPNode followXPathStep(
        XMPNode parentNode,
        XMPPathSegment nextStep,
        boolean createNodes) throws XMPException
  {
    XMPNode nextNode = null;
    int index = 0;
    int stepKind = nextStep.getKind();
   
    if (stepKind == XMPPath.STRUCT_FIELD_STEP)
    {
      nextNode = findChildNode(parentNode, nextStep.getName(), createNodes);
    }
    else if (stepKind == XMPPath.QUALIFIER_STEP)
    {
      nextNode = findQualifierNode(
        parentNode, nextStep.getName().substring(1), createNodes);
    }
    else
    {
      // This is an array indexing step. First get the index, then get the node.

      if (!parentNode.getOptions().isArray())
      {
        throw new XMPException("Indexing applied to non-array", XMPError.BADXPATH);
      }

      if (stepKind == XMPPath.ARRAY_INDEX_STEP)
      {
        index = findIndexedItem(parentNode, nextStep.getName(), createNodes);
      }
      else if (stepKind == XMPPath.ARRAY_LAST_STEP)
      {
        index = parentNode.getChildrenLength();
      }
      else if (stepKind == XMPPath.FIELD_SELECTOR_STEP)
      {
        String[] result = Utils.splitNameAndValue(nextStep.getName());
        String fieldName = result[0];
        String fieldValue = result[1];
        index = lookupFieldSelector(parentNode, fieldName, fieldValue);
      }
      else if (stepKind == XMPPath.QUAL_SELECTOR_STEP)
      {
        String[] result = Utils.splitNameAndValue(nextStep.getName());
        String qualName = result[0];
        String qualValue = result[1];
        index = lookupQualSelector(
          parentNode, qualName, qualValue, nextStep.getAliasForm());
      }
      else
      {
        throw new XMPException("Unknown array indexing step in FollowXPathStep",
            XMPError.INTERNALFAILURE);
      }

      if (1 <= index  &&  index <=  parentNode.getChildrenLength())
      {
        nextNode = parentNode.getChild(index);
      }
    }
 
    return nextNode;   
  }


  /**
   * Find or create a qualifier node under a given parent node. Returns a pointer to the
   * qualifier node, and optionally an iterator for the node's position in
   * the parent's vector of qualifiers. The iterator is unchanged if no qualifier node (null)
   * is returned.
   * <em>Note:</em> On entry, the qualName parameter must not have the leading '?' from the
   * XMPPath step.
   *
   * @param parent the parent XMPNode
   * @param qualName the qualifier name
   * @param createNodes flag if nodes shall be created
   * @return Returns the qualifier node if found or created, <code>null</code> otherwise.
   * @throws XMPException
   */
  private static XMPNode findQualifierNode(XMPNode parent, String qualName, boolean createNodes)
      throws XMPException
  {
    assert !qualName.startsWith("?");
   
    XMPNode qualNode = parent.findQualifierByName(qualName);
   
    if (qualNode == null  &&  createNodes)
    {
      qualNode = new XMPNode(qualName, null);
      qualNode.setImplicit(true);
 
      parent.addQualifier(qualNode);       
    }
   
    return qualNode;
  }
 
 
  /**
   * @param arrayNode an array node
   * @param segment the segment containing the array index
   * @param createNodes flag if new nodes are allowed to be created.
   * @return Returns the index or index = -1 if not found
   * @throws XMPException Throws Exceptions
   */
  private static int findIndexedItem(XMPNode arrayNode, String segment, boolean createNodes)
      throws XMPException
  {
    int index = 0;
 
    try
    {
      segment = segment.substring(1, segment.length() - 1);
      index = Integer.parseInt(segment);
      if (index < 1)
      {
        throw new XMPException("Array index must be larger than zero",
            XMPError.BADXPATH);
      }
    }
    catch (NumberFormatException e)
    {
      throw new XMPException("Array index not digits.", XMPError.BADXPATH);
    }
 
    if (createNodes  &&  index == arrayNode.getChildrenLength() + 1)
    {
      // Append a new last + 1 node.
      XMPNode newItem = new XMPNode(ARRAY_ITEM_NAME, null);
      newItem.setImplicit(true);
      arrayNode.addChild(newItem);
    }

    return index;
  }
 
 
  /**
   * Searches for a field selector in a node:
   * [fieldName="value] - an element in an array of structs, chosen by a field value.
   * No implicit nodes are created by field selectors.
   *
   * @param arrayNode
   * @param fieldName
   * @param fieldValue
   * @return Returns the index of the field if found, otherwise -1.
   * @throws XMPException
   */
  private static int lookupFieldSelector(XMPNode arrayNode, String fieldName, String fieldValue)
    throws XMPException
  {
    int result = -1;
   
    for (int index = 1; index <= arrayNode.getChildrenLength()  &&  result < 0; index++)
    {
      XMPNode currItem = arrayNode.getChild(index);
 
      if (!currItem.getOptions().isStruct())
      {
        throw new XMPException("Field selector must be used on array of struct",
            XMPError.BADXPATH);
      }
 
      for (int f = 1; f <= currItem.getChildrenLength(); f++)
      {
        XMPNode currField = currItem.getChild(f);
        if (!fieldName.equals(currField.getName()))
        {
          continue;
        }
        if (fieldValue.equals(currField.getValue()))
        {
          result = index;
          break;
        }
      }
    }
   
    return result;
  }
 
 
  /**
   * Searches for a qualifier selector in a node:
   * [?qualName="value"] - an element in an array, chosen by a qualifier value.
   * No implicit nodes are created for qualifier selectors,
   * except for an alias to an x-default item.
   *
   * @param arrayNode an array node
   * @param qualName the qualifier name
   * @param qualValue the qualifier value
   * @param aliasForm in case the qual selector results from an alias,
   *       an x-default node is created if there has not been one.
   * @return Returns the index of th
   * @throws XMPException
   */
  private static int lookupQualSelector(XMPNode arrayNode, String qualName,
    String qualValue, int aliasForm) throws XMPException
  {
    if (XML_LANG.equals(qualName))
    {
      qualValue = Utils.normalizeLangValue(qualValue);
      int index = XMPNodeUtils.lookupLanguageItem(arrayNode, qualValue);
      if (index < &&  (aliasForm & AliasOptions.PROP_ARRAY_ALT_TEXT) > 0)
      { 
        XMPNode langNode = new XMPNode(ARRAY_ITEM_NAME, null);
        XMPNode xdefault = new XMPNode(XML_LANG, X_DEFAULT, null);
        langNode.addQualifier(xdefault);
        arrayNode.addChild(1, langNode);
        return 1;
      }
      else
      {
        return index;
      }
    }
    else
    {
      for (int index = 1; index < arrayNode.getChildrenLength(); index++)
      {
        XMPNode currItem = arrayNode.getChild(index);
   
        for (Iterator it = currItem.iterateQualifier(); it.hasNext();)
        {
          XMPNode qualifier = (XMPNode) it.next();
          if (qualName.equals(qualifier.getName())  &&
            qualValue.equals(qualifier.getValue()))
          {
            return index;
          }
        }
      }
      return -1;
   
  }


  /**
   * Make sure the x-default item is first. Touch up &quot;single value&quot;
   * arrays that have a default plus one real language. This case should have
   * the same value for both items. Older Adobe apps were hardwired to only
   * use the &quot;x-default&quot; item, so we copy that value to the other
   * item.
   *
   * @param arrayNode
   *            an alt text array node
   */
  static void normalizeLangArray(XMPNode arrayNode)
  {
    if (!arrayNode.getOptions().isArrayAltText())
    {
      return;
    }
 
    // check if node with x-default qual is first place
    for (int i = 2; i <= arrayNode.getChildrenLength(); i++)
    {
      XMPNode child = arrayNode.getChild(i);
      if (child.hasQualifier() && X_DEFAULT.equals(child.getQualifier(1).getValue()))
      {
        // move node to first place
        try
        {
          arrayNode.removeChild(i);
          arrayNode.addChild(1, child);
        }
        catch (XMPException e)
        {
          // cannot occur, because same child is removed before
          assert false;
        }
       
        if (i == 2)
        {
          arrayNode.getChild(2).setValue(child.getValue());
        }
        break;
      }
    }
  }


  /**
   * See if an array is an alt-text array. If so, make sure the x-default item
   * is first.
   *
   * @param arrayNode
   *            the array node to check if its an alt-text array
   */
  static void detectAltText(XMPNode arrayNode)
  {
    if (arrayNode.getOptions().isArrayAlternate() && arrayNode.hasChildren())
    {
      boolean isAltText = false;
      for (Iterator it = arrayNode.iterateChildren(); it.hasNext();)
      {
        XMPNode child = (XMPNode) it.next();
        if (child.getOptions().getHasLanguage())
        {
          isAltText = true;
          break;
        }
      }
 
      if (isAltText)
      {
        arrayNode.getOptions().setArrayAltText(true);
        normalizeLangArray(arrayNode);
      }
    }
  }


  /**
   * Appends a language item to an alt text array.
   *
   * @param arrayNode the language array
   * @param itemLang the language of the item
   * @param itemValue the content of the item
   * @throws XMPException Thrown if a duplicate property is added
   */
  static void appendLangItem(XMPNode arrayNode, String itemLang, String itemValue)
      throws XMPException
  {
    XMPNode newItem = new XMPNode(ARRAY_ITEM_NAME, itemValue, null);
    XMPNode langQual = new XMPNode(XML_LANG, itemLang, null);
    newItem.addQualifier(langQual);
 
    if (!X_DEFAULT.equals(langQual.getValue()))
    {
      arrayNode.addChild(newItem);
    }
    else
    {
      arrayNode.addChild(1, newItem);
    }
  }


  /**
   * <ol>
   * <li>Look for an exact match with the specific language.
   * <li>If a generic language is given, look for partial matches.
   * <li>Look for an "x-default"-item.
   * <li>Choose the first item.
   * </ol>
   *
   * @param arrayNode
   *            the alt text array node
   * @param genericLang
   *            the generic language
   * @param specificLang
   *            the specific language
   * @return Returns the kind of match as an Integer and the found node in an
   *         array.
   *
   * @throws XMPException
   */
  static Object[] chooseLocalizedText(XMPNode arrayNode, String genericLang, String specificLang)
      throws XMPException
  {
    // See if the array has the right form. Allow empty alt arrays,
    // that is what parsing returns.
    if (!arrayNode.getOptions().isArrayAltText())
    {
      throw new XMPException("Localized text array is not alt-text", XMPError.BADXPATH);
    }
    else if (!arrayNode.hasChildren())
    {
      return new Object[] { new Integer(XMPNodeUtils.CLT_NO_VALUES), null };
    }
 
    int foundGenericMatches = 0;
    XMPNode resultNode = null;
    XMPNode xDefault = null;
 
    // Look for the first partial match with the generic language.
    for (Iterator it = arrayNode.iterateChildren(); it.hasNext();)
    {
      XMPNode currItem = (XMPNode) it.next();
 
      // perform some checks on the current item
      if (currItem.getOptions().isCompositeProperty())
      {
        throw new XMPException("Alt-text array item is not simple", XMPError.BADXPATH);
      }
      else if (!currItem.hasQualifier()
          || !XML_LANG.equals(currItem.getQualifier(1).getName()))
      {
        throw new XMPException("Alt-text array item has no language qualifier",
            XMPError.BADXPATH);
      }
 
      String currLang = currItem.getQualifier(1).getValue();
 
      // Look for an exact match with the specific language.
      if (specificLang.equals(currLang))
      {
        return new Object[] { new Integer(XMPNodeUtils.CLT_SPECIFIC_MATCH), currItem };
      }
      else if (genericLang != null && currLang.startsWith(genericLang))
      {
        if (resultNode == null)
        {
          resultNode = currItem;
        }
        // ! Don't return/break, need to look for other matches.
        foundGenericMatches++;
      }
      else if (X_DEFAULT.equals(currLang))
      {
        xDefault = currItem;
      }
    }
 
    // evaluate loop
    if (foundGenericMatches == 1)
    {
      return new Object[] { new Integer(XMPNodeUtils.CLT_SINGLE_GENERIC), resultNode };
    }
    else if (foundGenericMatches > 1)
    {
      return new Object[] { new Integer(XMPNodeUtils.CLT_MULTIPLE_GENERIC), resultNode };
    }
    else if (xDefault != null)
    {
      return new Object[] { new Integer(XMPNodeUtils.CLT_XDEFAULT), xDefault };
    }
    else
    {
      // Everything failed, choose the first item.
      return new Object[] { new Integer(XMPNodeUtils.CLT_FIRST_ITEM), arrayNode.getChild(1) };
    }
  }


  /**
   * Looks for the appropriate language item in a text alternative array.item
   *
   * @param arrayNode
   *            an array node
   * @param language
   *            the requested language
   * @return Returns the index if the language has been found, -1 otherwise.
   * @throws XMPException
   */
  static int lookupLanguageItem(XMPNode arrayNode, String language) throws XMPException
  {
    if (!arrayNode.getOptions().isArray())
    {
      throw new XMPException("Language item must be used on array", XMPError.BADXPATH);
    }
 
    for (int index = 1; index <= arrayNode.getChildrenLength(); index++)
    {
      XMPNode child = arrayNode.getChild(index);
      if (!child.hasQualifier() || !XML_LANG.equals(child.getQualifier(1).getName()))
      {
        continue;
      }
      else if (language.equals(child.getQualifier(1).getValue()))
      {
        return index;
      }
    }
 
    return -1;
  }
}
TOP

Related Classes of com.adobe.xmp.impl.XMPNodeUtils

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.