Package com.adobe.xmp.impl

Source Code of com.adobe.xmp.impl.XMPIteratorImpl$NodeIteratorChildren

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

import com.adobe.xmp.XMPError;
import com.adobe.xmp.XMPException;
import com.adobe.xmp.XMPIterator;
import com.adobe.xmp.XMPMetaFactory;
import com.adobe.xmp.impl.xpath.XMPPath;
import com.adobe.xmp.impl.xpath.XMPPathParser;
import com.adobe.xmp.options.IteratorOptions;
import com.adobe.xmp.options.PropertyOptions;
import com.adobe.xmp.properties.XMPPropertyInfo;


/**
* The <code>XMPIterator</code> implementation.
* Iterates the XMP Tree according to a set of options.
* During the iteration the XMPMeta-object must not be changed.
* Calls to <code>skipSubtree()</code> / <code>skipSiblings()</code> will affect the iteration.
* @since   29.06.2006
*/
public class XMPIteratorImpl implements XMPIterator
{
  /** stores the iterator options */
  private IteratorOptions options;
  /** the base namespace of the property path, will be changed during the iteration */
  private String baseNS = null;
  /** flag to indicate that skipSiblings() has been called. */
  protected boolean skipSiblings = false;
  /** flag to indicate that skipSiblings() has been called. */
  protected boolean skipSubtree = false;
  /** the node iterator doing the work */
  private Iterator nodeIterator = null;
 
 
  /**
   * Constructor with optionsl initial values. If <code>propName</code> is provided,
   * <code>schemaNS</code> has also be provided.
   * @param xmp the iterated metadata object.
   * @param schemaNS the iteration is reduced to this schema (optional)
   * @param propPath the iteration is redurce to this property within the <code>schemaNS</code>
   * @param options advanced iteration options, see {@link IteratorOptions}
   * @throws XMPException If the node defined by the paramters is not existing.
   */
  public XMPIteratorImpl(XMPMetaImpl xmp, String schemaNS, String propPath,
      IteratorOptions options) throws XMPException
  {
    // make sure that options is defined at least with defaults
    this.options = options != null ? options : new IteratorOptions();
   
    // the start node of the iteration depending on the schema and property filter
    XMPNode startNode = null;
    String initialPath = null;
    boolean baseSchema = schemaNS != null  &&  schemaNS.length() > 0;
    boolean baseProperty = propPath != null  &&  propPath.length() > 0;
   
    if (!baseSchema  &&  !baseProperty)
    {
      // complete tree will be iterated
      startNode = xmp.getRoot();
    }
    else if (baseSchema  &&  baseProperty)
    {
      // Schema and property node provided
      XMPPath path = XMPPathParser.expandXPath(schemaNS, propPath);
     
      // base path is the prop path without the property leaf
      XMPPath basePath = new XMPPath();
      for (int i = 0; i < path.size() - 1; i++)
      {
        basePath.add(path.getSegment(i));
      }
     
      startNode = XMPNodeUtils.findNode(xmp.getRoot(), path, false, null);
      baseNS = schemaNS;
      initialPath = basePath.toString();
    }
    else if (baseSchema  &&  !baseProperty)
    {
      // Only Schema provided
      startNode = XMPNodeUtils.findSchemaNode(xmp.getRoot(), schemaNS, false);
    }
    else // !baseSchema  &&  baseProperty
    {
      // No schema but property provided -> error
      throw new XMPException("Schema namespace URI is required", XMPError.BADSCHEMA);
    }     

   
    // create iterator
    if (startNode != null)
    {
      if (!this.options.isJustChildren())
      { 
        nodeIterator = new NodeIterator(startNode, initialPath, 1);
      }
      else
      {
        nodeIterator = new NodeIteratorChildren(startNode, initialPath);
      }
    }
    else
    {
      // create null iterator
      nodeIterator = Collections.EMPTY_LIST.iterator();
    }
  }


  /**
   * @see XMPIterator#skipSubtree()
   */
  public void skipSubtree()
  {
    this.skipSubtree = true;
  }

 
  /**
   * @see XMPIterator#skipSiblings()
   */
  public void skipSiblings()
  {
    skipSubtree();
    this.skipSiblings = true;
  }
 

  /**
   * @see java.util.Iterator#hasNext()
   */
  public boolean hasNext()
  {
    return nodeIterator.hasNext();
  }

 
  /**
   * @see java.util.Iterator#next()
   */
  public Object next()
  {
    return nodeIterator.next();
  }

 
  /**
   * @see java.util.Iterator#remove()
   */
  public void remove()
  {
    throw new UnsupportedOperationException("The XMPIterator does not support remove().");
  }
 
 
  /**
   * @return Exposes the options for inner class.
   */
  protected IteratorOptions getOptions()
  {
    return options;
  }

 
  /**
   * @return Exposes the options for inner class.
   */
  protected String getBaseNS()
  {
    return baseNS;
  }

 
  /**
   * @param baseNS sets the baseNS from the inner class.
   */
  protected void setBaseNS(String baseNS)
  {
    this.baseNS = baseNS;
  }
 
 
 
 
 
 
  /**
   * The <code>XMPIterator</code> implementation.
   * It first returns the node itself, then recursivly the children and qualifier of the node.
   *
   * @since   29.06.2006
   */
  private class NodeIterator implements Iterator
  {
    /** iteration state */
    protected static final int ITERATE_NODE = 0;
    /** iteration state */
    protected static final int ITERATE_CHILDREN = 1;
    /** iteration state */
    protected static final int ITERATE_QUALIFIER = 2;
   
    /** the state of the iteration */
    private int state = ITERATE_NODE;
    /** the currently visited node */
    private XMPNode visitedNode;
    /** the recursively accumulated path */
    private String path;
    /** the iterator that goes through the children and qualifier list */
    private Iterator childrenIterator = null;
    /** index of node with parent, only interesting for arrays */
    private int index = 0;
    /** the iterator for each child */
    private Iterator subIterator = Collections.EMPTY_LIST.iterator();
    /** the cached <code>PropertyInfo</code> to return */
    private XMPPropertyInfo returnProperty = null;

   
    /**
     * Default constructor
     */
    public NodeIterator()
    {
      // EMPTY
    }
   
   
    /**
     * Constructor for the node iterator.
     * @param visitedNode the currently visited node
     * @param parentPath the accumulated path of the node
     * @param index the index within the parent node (only for arrays)
     */
    public NodeIterator(XMPNode visitedNode, String parentPath, int index)
    {
      this.visitedNode = visitedNode;
      this.state = NodeIterator.ITERATE_NODE;
      if (visitedNode.getOptions().isSchemaNode())
      { 
        setBaseNS(visitedNode.getName());
      }

      // for all but the root node and schema nodes
      path = accumulatePath(visitedNode, parentPath, index);
    }

   
    /**
     * Prepares the next node to return if not already done.
     *
     * @see Iterator#hasNext()
     */
    public boolean hasNext()
    {
      if (returnProperty != null)
      {
        // hasNext has been called before
        return true;
      }
     
      // find next node
      if (state == ITERATE_NODE)
      {
        return reportNode();
      }
      else if (state == ITERATE_CHILDREN)
      {
        if (childrenIterator == null)
        {
          childrenIterator = visitedNode.iterateChildren();
        }
       
        boolean hasNext = iterateChildren(childrenIterator);
       
        if (!hasNext  &&  visitedNode.hasQualifier()  &&  !getOptions().isOmitQualifiers())
        {
          state = ITERATE_QUALIFIER;
          childrenIterator = null;
          hasNext = hasNext();
        }
        return hasNext;
      }
      else
      {
        if (childrenIterator == null)
        {
          childrenIterator = visitedNode.iterateQualifier();
        }
       
        return iterateChildren(childrenIterator);
      }
    }


    /**
     * Sets the returnProperty as next item or recurses into <code>hasNext()</code>.
     * @return Returns if there is a next item to return.
     */
    protected boolean reportNode()
    {
      state = ITERATE_CHILDREN;
      if (visitedNode.getParent() != null  &&
        (!getOptions().isJustLeafnodes()  ||  !visitedNode.hasChildren()))
      { 
        returnProperty = createPropertyInfo(visitedNode, getBaseNS(), path);
        return true;
      }
      else
      {
        return hasNext();
      }
    }


    /**
     * Handles the iteration of the children or qualfier
     * @param iterator an iterator
     * @return Returns if there are more elements available.
     */
    private boolean iterateChildren(Iterator iterator)
    {
      if (skipSiblings)
      {
        // setSkipSiblings(false);
        skipSiblings = false;
        subIterator = Collections.EMPTY_LIST.iterator();
      }
     
      // create sub iterator for every child,
      // if its the first child visited or the former child is finished
      if ((!subIterator.hasNext())  &&  iterator.hasNext())
      {
        XMPNode child = (XMPNode) iterator.next();
        index++;
        subIterator = new NodeIterator(child, path, index);
      }
     
      if (subIterator.hasNext())
      {
        returnProperty = (XMPPropertyInfo) subIterator.next();
        return true;
      }
      else
      {
        return false;
      }
    }
 
   
    /**
     * Calls hasNext() and returnes the prepared node. Afterwards its set to null.
     * The existance of returnProperty indicates if there is a next node, otherwise
     * an exceptio is thrown.
     *
     * @see Iterator#next()
     */
    public Object next()
    {
      if (hasNext())
      {
        XMPPropertyInfo result = returnProperty;
        returnProperty = null;
        return result;
      }
      else
      {
        throw new NoSuchElementException("There are no more nodes to return");
      }
    }
 
   
    /**
     * Not supported.
     * @see Iterator#remove()
     */
    public void remove()
    {
      throw new UnsupportedOperationException();
    }
   
   
    /**
     * @param currNode the node that will be added to the path.
     * @param parentPath the path up to this node.
     * @param currentIndex the current array index if an arrey is traversed
     * @return Returns the updated path.
     */
    protected String accumulatePath(XMPNode currNode, String parentPath, int currentIndex)
    {
      String separator;
      String segmentName;
      if (currNode.getParent() == null  ||  currNode.getOptions().isSchemaNode())
      {
        return null;
      }
      else if (currNode.getParent().getOptions().isArray())
      {
        separator = "";
        segmentName = "[" + String.valueOf(currentIndex) + "]";
      }
      else
     
        separator = "/";
        segmentName = currNode.getName();
      }
     
     
      if (parentPath == null  ||  parentPath.length() == 0)
      {
        return segmentName;
      }
      else if (getOptions().isJustLeafname())
      {
        return !segmentName.startsWith("?") ?
          segmentName :
          segmentName.substring(1); // qualifier
      }
      else
      {
        return parentPath + separator + segmentName;
      }
    }
     
   
    /**
     * Creates a property info object from an <code>XMPNode</code>.
     * @param node an <code>XMPNode</code>
     * @param baseNS the base namespace to report
     * @param path the full property path
     * @return Returns a <code>XMPProperty</code>-object that serves representation of the node.
     */
    protected XMPPropertyInfo createPropertyInfo(final XMPNode node, final String baseNS,
        final String path)
    {
      final String value = node.getOptions().isSchemaNode() ? null : node.getValue();
     
      return new XMPPropertyInfo()
      {
        public String getNamespace()
        {
          if (!node.getOptions().isSchemaNode())
          { 
            // determine namespace of leaf node
            QName qname = new QName(node.getName());
            return XMPMetaFactory.getSchemaRegistry().getNamespaceURI(qname.getPrefix());
          }
          else
          {
            return baseNS;
         
        }
 
        public String getPath()
        {
          return path;
        }
       
        public String getValue()
        {
          return value;
        }
 
        public PropertyOptions getOptions()
        {
          return node.getOptions();
        }

        public String getLanguage()
        {
          // the language is not reported
          return null;
        }
      };
    }


    /**
     * @return the childrenIterator
     */
    protected  Iterator getChildrenIterator()
    {
      return childrenIterator;
    }


    /**
     * @param childrenIterator the childrenIterator to set
     */
    protected void setChildrenIterator(Iterator childrenIterator)
    {
      this.childrenIterator = childrenIterator;
    }

   
    /**
     * @return Returns the returnProperty.
     */
    protected XMPPropertyInfo getReturnProperty()
    {
      return returnProperty;
    }
   

    /**
     * @param returnProperty the returnProperty to set
     */
    protected  void setReturnProperty(XMPPropertyInfo returnProperty)
    {
      this.returnProperty = returnProperty;
    }
  }
 
 
  /**
   * This iterator is derived from the default <code>NodeIterator</code>,
   * and is only used for the option {@link IteratorOptions#JUST_CHILDREN}.
   *
   * @since 02.10.2006
   */
  private class NodeIteratorChildren extends NodeIterator
  {
    /** */
    private String parentPath;
    /** */
    private Iterator childrenIterator;
    /** */
    private int index = 0;
   
   
    /**
     * Constructor
     * @param parentNode the node which children shall be iterated.
     * @param parentPath the full path of the former node without the leaf node.
     */
    public NodeIteratorChildren(XMPNode parentNode, String parentPath)
    {
      if (parentNode.getOptions().isSchemaNode())
      { 
        setBaseNS(parentNode.getName());
      }
      this.parentPath = accumulatePath(parentNode, parentPath, 1);

      childrenIterator = parentNode.iterateChildren();
    }
   
   
    /**
     * Prepares the next node to return if not already done.
     *
     * @see Iterator#hasNext()
     */
    public boolean hasNext()
    {
      if (getReturnProperty() != null)
      {
        // hasNext has been called before
        return true;
      }
      else if (skipSiblings)
      {
        return false;
      }
      else if (childrenIterator.hasNext())
      {
        XMPNode child = (XMPNode) childrenIterator.next();
        index++;
       
        String path = null;
        if (child.getOptions().isSchemaNode())
        { 
          setBaseNS(child.getName());
        }
        else if (child.getParent() != null)
        {
          // for all but the root node and schema nodes
          path = accumulatePath(child, parentPath, index);
        }

        // report next property, skip not-leaf nodes in case options is set
        if (!getOptions().isJustLeafnodes()  ||  !child.hasChildren())
        { 
          setReturnProperty(createPropertyInfo(child, getBaseNS(), path));
          return true;
        }
        else
        {
          return hasNext();
        }
      }
      else
      {
        return false;
      }
    }   
  }
}
TOP

Related Classes of com.adobe.xmp.impl.XMPIteratorImpl$NodeIteratorChildren

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.