com.adobe.xmp.impl.XMPIteratorImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xmpcore Show documentation
Show all versions of xmpcore Show documentation
The XMP Library for Java is based on the C++ XMPCore library
and the API is similar.
// =================================================================================================
// 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 XMPIterator
implementation.
* Iterates the XMP Tree according to a set of options.
* During the iteration the XMPMeta-object must not be changed.
* Calls to skipSubtree()
/ skipSiblings()
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 propName
is provided,
* schemaNS
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 schemaNS
* @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 XMPIterator
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 PropertyInfo
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 hasNext()
.
* @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 XMPNode
.
* @param node an XMPNode
* @param baseNS the base namespace to report
* @param path the full property path
* @return Returns a XMPProperty
-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 NodeIterator
,
* 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;
}
}
}
}