📄 xmpnodeutils.java
字号:
// =================================================================================================// ADOBE SYSTEMS INCORPORATED// Copyright 2006-2007 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.Calendar;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 == 1 && 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() - 1 && 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 Calendar) { XMPDateTime dt = XMPDateTimeFactory.createFromCalendar((Calendar) 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -