*
*
* <ns:UnqualifiedStructProperty-1
* ... The fields as attributes, if all are simple and unqualified
* />
*
* <ns:UnqualifiedStructProperty-2 rdf:parseType="Resource">
* ... The fields as elements, if none are simple and unqualified
* </ns:UnqualifiedStructProperty-2>
*
* <ns:UnqualifiedStructProperty-3>
* <rdf:Description
* ... The simple and unqualified fields as attributes
* >
* ... The compound or qualified fields as elements
* </rdf:Description>
* </ns:UnqualifiedStructProperty-3>
*
* <ns:UnqualifiedArrayProperty>
* <rdf:Bag> or Seq or Alt
* ... Array items as rdf:li elements, same forms as top level properties
* </rdf:Bag>
* </ns:UnqualifiedArrayProperty>
*
* <ns:QualifiedProperty rdf:parseType="Resource">
* <rdf:value> ... Property "value"
* following the unqualified forms ... </rdf:value>
* ... Qualifiers looking like named struct fields
* </ns:QualifiedProperty>
*
*
*
*
* *** Consider numbered array items, but has compatibility problems. ***
* Consider qualified form with rdf:Description and attributes.
*
* @param parentNode the parent node
* @param indent the current indent level
* @throws IOException Forwards writer exceptions
* @throws XMPException If qualifier and element fields are mixed.
*/
private void serializeCompactRDFElementProps(XMPNode parentNode, int indent)
throws IOException, XMPException
{
for (Iterator it = parentNode.iterateChildren(); it.hasNext();)
{
XMPNode node = (XMPNode) it.next();
if (canBeRDFAttrProp (node))
{
continue;
}
boolean emitEndTag = true;
boolean indentEndTag = true;
// Determine the XML element name, write the name part of the start tag. Look over the
// qualifiers to decide on "normal" versus "rdf:value" form. Emit the attribute
// qualifiers at the same time.
String elemName = node.getName();
if (XMPConst.ARRAY_ITEM_NAME.equals(elemName))
{
elemName = "rdf:li";
}
writeIndent(indent);
write('<');
write(elemName);
boolean hasGeneralQualifiers = false;
boolean hasRDFResourceQual = false;
for (Iterator iq = node.iterateQualifier(); iq.hasNext();)
{
XMPNode qualifier = (XMPNode) iq.next();
if (!RDF_ATTR_QUALIFIER.contains(qualifier.getName()))
{
hasGeneralQualifiers = true;
}
else
{
hasRDFResourceQual = "rdf:resource".equals(qualifier.getName());
write(' ');
write(qualifier.getName());
write("=\"");
appendNodeValue(qualifier.getValue(), true);
write('"');
}
}
// Process the property according to the standard patterns.
if (hasGeneralQualifiers)
{
serializeCompactRDFGeneralQualifier(indent, node);
}
else
{
// This node has only attribute qualifiers. Emit as a property element.
if (!node.getOptions().isCompositeProperty())
{
Object[] result = serializeCompactRDFSimpleProp(node);
emitEndTag = ((Boolean) result[0]).booleanValue();
indentEndTag = ((Boolean) result[1]).booleanValue();
}
else if (node.getOptions().isArray())
{
serializeCompactRDFArrayProp(node, indent);
}
else
{
emitEndTag = serializeCompactRDFStructProp(
node, indent, hasRDFResourceQual);
}
}
// Emit the property element end tag.
if (emitEndTag)
{
if (indentEndTag)
{
writeIndent(indent);
}
write("");
write(elemName);
write('>');
writeNewline();
}
}
}
/**
* Serializes a simple property.
*
* @param node an XMPNode
* @return Returns an array containing the flags emitEndTag and indentEndTag.
* @throws IOException Forwards the writer exceptions.
*/
private Object[] serializeCompactRDFSimpleProp(XMPNode node) throws IOException
{
// This is a simple property.
Boolean emitEndTag = Boolean.TRUE;
Boolean indentEndTag = Boolean.TRUE;
if (node.getOptions().isURI())
{
write(" rdf:resource=\"");
appendNodeValue(node.getValue(), true);
write("\"/>");
writeNewline();
emitEndTag = Boolean.FALSE;
}
else if (node.getValue() == null || node.getValue().length() == 0)
{
write("/>");
writeNewline();
emitEndTag = Boolean.FALSE;
}
else
{
write('>');
appendNodeValue (node.getValue(), false);
indentEndTag = Boolean.FALSE;
}
return new Object[] {emitEndTag, indentEndTag};
}
/**
* Serializes an array property.
*
* @param node an XMPNode
* @param indent the current indent level
* @throws IOException Forwards the writer exceptions.
* @throws XMPException If qualifier and element fields are mixed.
*/
private void serializeCompactRDFArrayProp(XMPNode node, int indent) throws IOException,
XMPException
{
// This is an array.
write('>');
writeNewline();
emitRDFArrayTag (node, true, indent + 1);
if (node.getOptions().isArrayAltText())
{
XMPNodeUtils.normalizeLangArray (node);
}
serializeCompactRDFElementProps(node, indent + 2);
emitRDFArrayTag(node, false, indent + 1);
}
/**
* Serializes a struct property.
*
* @param node an XMPNode
* @param indent the current indent level
* @param hasRDFResourceQual Flag if the element has resource qualifier
* @return Returns true if an end flag shall be emitted.
* @throws IOException Forwards the writer exceptions.
* @throws XMPException If qualifier and element fields are mixed.
*/
private boolean serializeCompactRDFStructProp(XMPNode node, int indent,
boolean hasRDFResourceQual) throws XMPException, IOException
{
// This must be a struct.
boolean hasAttrFields = false;
boolean hasElemFields = false;
boolean emitEndTag = true;
for (Iterator ic = node.iterateChildren(); ic.hasNext(); )
{
XMPNode field = (XMPNode) ic.next();
if (canBeRDFAttrProp(field))
{
hasAttrFields = true;
}
else
{
hasElemFields = true;
}
if (hasAttrFields && hasElemFields)
{
break; // No sense looking further.
}
}
if (hasRDFResourceQual && hasElemFields)
{
throw new XMPException(
"Can't mix rdf:resource qualifier and element fields",
XMPError.BADRDF);
}
if (!node.hasChildren())
{
// Catch an empty struct as a special case. The case
// below would emit an empty
// XML element, which gets reparsed as a simple property
// with an empty value.
write(" rdf:parseType=\"Resource\"/>");
writeNewline();
emitEndTag = false;
}
else if (!hasElemFields)
{
// All fields can be attributes, use the
// emptyPropertyElt form.
serializeCompactRDFAttrProps(node, indent + 1);
write("/>");
writeNewline();
emitEndTag = false;
}
else if (!hasAttrFields)
{
// All fields must be elements, use the
// parseTypeResourcePropertyElt form.
write(" rdf:parseType=\"Resource\">");
writeNewline();
serializeCompactRDFElementProps(node, indent + 1);
}
else
{
// Have a mix of attributes and elements, use an inner rdf:Description.
write('>');
writeNewline();
writeIndent(indent + 1);
write(RDF_STRUCT_START);
serializeCompactRDFAttrProps(node, indent + 2);
write(">");
writeNewline();
serializeCompactRDFElementProps(node, indent + 1);
writeIndent(indent + 1);
write(RDF_STRUCT_END);
writeNewline();
}
return emitEndTag;
}
/**
* Serializes the general qualifier.
* @param node the root node of the subtree
* @param indent the current indent level
* @throws IOException Forwards all writer exceptions.
* @throws XMPException If qualifier and element fields are mixed.
*/
private void serializeCompactRDFGeneralQualifier(int indent, XMPNode node)
throws IOException, XMPException
{
// The node has general qualifiers, ones that can't be
// attributes on a property element.
// Emit using the qualified property pseudo-struct form. The
// value is output by a call
// to SerializePrettyRDFProperty with emitAsRDFValue set.
write(" rdf:parseType=\"Resource\">");
writeNewline();
serializePrettyRDFProperty(node, true, indent + 1);
for (Iterator iq = node.iterateQualifier(); iq.hasNext();)
{
XMPNode qualifier = (XMPNode) iq.next();
serializePrettyRDFProperty(qualifier, false, indent + 1);
}
}
/**
* Serializes one schema with all contained properties in pretty-printed
* manner.
*
*
* <ns:UnqualifiedSimpleProperty>value</ns:UnqualifiedSimpleProperty>
*
* <ns:UnqualifiedStructProperty rdf:parseType="Resource">
* (If no rdf:resource qualifier)
* ... Fields, same forms as top level properties
* </ns:UnqualifiedStructProperty>
*
* <ns:ResourceStructProperty rdf:resource="URI"
* ... Fields as attributes
* >
*
* <ns:UnqualifiedArrayProperty>
* <rdf:Bag> or Seq or Alt
* ... Array items as rdf:li elements, same forms as top level properties
* </rdf:Bag>
* </ns:UnqualifiedArrayProperty>
*
* <ns:QualifiedProperty rdf:parseType="Resource">
* <rdf:value> ... Property "value" following the unqualified
* forms ... </rdf:value>
* ... Qualifiers looking like named struct fields
* </ns:QualifiedProperty>
*
*
*
*
* @param node the property node
* @param emitAsRDFValue property shall be renderes as attribute rather than tag
* @param indent the current indent level
* @throws IOException Forwards all writer exceptions.
* @throws XMPException If "rdf:resource" and general qualifiers are mixed.
*/
private void serializePrettyRDFProperty(XMPNode node, boolean emitAsRDFValue, int indent)
throws IOException, XMPException
{
boolean emitEndTag = true;
boolean indentEndTag = true;
// Determine the XML element name. Open the start tag with the name and
// attribute qualifiers.
String elemName = node.getName();
if (emitAsRDFValue)
{
elemName = "rdf:value";
}
else if (XMPConst.ARRAY_ITEM_NAME.equals(elemName))
{
elemName = "rdf:li";
}
writeIndent(indent);
write('<');
write(elemName);
boolean hasGeneralQualifiers = false;
boolean hasRDFResourceQual = false;
for (Iterator it = node.iterateQualifier(); it.hasNext();)
{
XMPNode qualifier = (XMPNode) it.next();
if (!RDF_ATTR_QUALIFIER.contains(qualifier.getName()))
{
hasGeneralQualifiers = true;
}
else
{
hasRDFResourceQual = "rdf:resource".equals(qualifier.getName());
if (!emitAsRDFValue)
{
write(' ');
write(qualifier.getName());
write("=\"");
appendNodeValue(qualifier.getValue(), true);
write('"');
}
}
}
// Process the property according to the standard patterns.
if (hasGeneralQualifiers && !emitAsRDFValue)
{
// This node has general, non-attribute, qualifiers. Emit using the
// qualified property form.
// ! The value is output by a recursive call ON THE SAME NODE with
// emitAsRDFValue set.
if (hasRDFResourceQual)
{
throw new XMPException("Can't mix rdf:resource and general qualifiers",
XMPError.BADRDF);
}
write(" rdf:parseType=\"Resource\">");
writeNewline();
serializePrettyRDFProperty(node, true, indent + 1);
for (Iterator it = node.iterateQualifier(); it.hasNext();)
{
XMPNode qualifier = (XMPNode) it.next();
if (!RDF_ATTR_QUALIFIER.contains(qualifier.getName()))
{
serializePrettyRDFProperty(qualifier, false, indent + 1);
}
}
}
else
{
// This node has no general qualifiers. Emit using an unqualified form.
if (!node.getOptions().isCompositeProperty())
{
// This is a simple property.
if (node.getOptions().isURI())
{
write(" rdf:resource=\"");
appendNodeValue(node.getValue(), true);
write("\"/>");
writeNewline();
emitEndTag = false;
}
else if (node.getValue() == null || "".equals(node.getValue()))
{
write("/>");
writeNewline();
emitEndTag = false;
}
else
{
write('>');
appendNodeValue(node.getValue(), false);
indentEndTag = false;
}
}
else if (node.getOptions().isArray())
{
// This is an array.
write('>');
writeNewline();
emitRDFArrayTag(node, true, indent + 1);
if (node.getOptions().isArrayAltText())
{
XMPNodeUtils.normalizeLangArray(node);
}
for (Iterator it = node.iterateChildren(); it.hasNext();)
{
XMPNode child = (XMPNode) it.next();
serializePrettyRDFProperty(child, false, indent + 2);
}
emitRDFArrayTag(node, false, indent + 1);
}
else if (!hasRDFResourceQual)
{
// This is a "normal" struct, use the rdf:parseType="Resource" form.
if (!node.hasChildren())
{
write(" rdf:parseType=\"Resource\"/>");
writeNewline();
emitEndTag = false;
}
else
{
write(" rdf:parseType=\"Resource\">");
writeNewline();
for (Iterator it = node.iterateChildren(); it.hasNext();)
{
XMPNode child = (XMPNode) it.next();
serializePrettyRDFProperty(child, false, indent + 1);
}
}
}
else
{
// This is a struct with an rdf:resource attribute, use the
// "empty property element" form.
for (Iterator it = node.iterateChildren(); it.hasNext();)
{
XMPNode child = (XMPNode) it.next();
if (!canBeRDFAttrProp(child))
{
throw new XMPException("Can't mix rdf:resource and complex fields",
XMPError.BADRDF);
}
writeNewline();
writeIndent(indent + 1);
write(' ');
write(child.getName());
write("=\"");
appendNodeValue(child.getValue(), true);
write('"');
}
write("/>");
writeNewline();
emitEndTag = false;
}
}
// Emit the property element end tag.
if (emitEndTag)
{
if (indentEndTag)
{
writeIndent(indent);
}
write("");
write(elemName);
write('>');
writeNewline();
}
}
/**
* Writes the array start and end tags.
*
* @param arrayNode an array node
* @param isStartTag flag if its the start or end tag
* @param indent the current indent level
* @throws IOException forwards writer exceptions
*/
private void emitRDFArrayTag(XMPNode arrayNode, boolean isStartTag, int indent)
throws IOException
{
if (isStartTag || arrayNode.hasChildren())
{
writeIndent(indent);
write(isStartTag ? "