• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // =================================================================================================
2 // ADOBE SYSTEMS INCORPORATED
3 // Copyright 2006 Adobe Systems Incorporated
4 // All Rights Reserved
5 //
6 // NOTICE:  Adobe permits you to use, modify, and distribute this file in accordance with the terms
7 // of the Adobe license agreement accompanying it.
8 // =================================================================================================
9 
10 package com.adobe.xmp.impl;
11 
12 import java.util.List;
13 import java.util.ArrayList;
14 import java.util.Iterator;
15 
16 import org.w3c.dom.Attr;
17 import org.w3c.dom.NamedNodeMap;
18 import org.w3c.dom.Node;
19 
20 import com.adobe.xmp.XMPConst;
21 import com.adobe.xmp.XMPError;
22 import com.adobe.xmp.XMPException;
23 import com.adobe.xmp.XMPMetaFactory;
24 import com.adobe.xmp.XMPSchemaRegistry;
25 import com.adobe.xmp.options.PropertyOptions;
26 
27 
28 /**
29  * Parser for "normal" XML serialisation of RDF.
30  *
31  * @since   14.07.2006
32  */
33 public class ParseRDF implements XMPError, XMPConst
34 {
35 	/** */
36 	public static final int RDFTERM_OTHER = 0;
37 	/** Start of coreSyntaxTerms. */
38 	public static final int RDFTERM_RDF = 1;
39 	/** */
40 	public static final int RDFTERM_ID = 2;
41 	/** */
42 	public static final int RDFTERM_ABOUT = 3;
43 	/** */
44 	public static final int RDFTERM_PARSE_TYPE = 4;
45 	/** */
46 	public static final int RDFTERM_RESOURCE = 5;
47 	/** */
48 	public static final int RDFTERM_NODE_ID = 6;
49 	/** End of coreSyntaxTerms */
50 	public static final int RDFTERM_DATATYPE = 7;
51 	/** Start of additions for syntax Terms. */
52 	public static final int RDFTERM_DESCRIPTION = 8;
53 	/** End of of additions for syntaxTerms. */
54 	public static final int RDFTERM_LI = 9;
55 	/** Start of oldTerms. */
56 	public static final int RDFTERM_ABOUT_EACH = 10;
57 	/** */
58 	public static final int RDFTERM_ABOUT_EACH_PREFIX = 11;
59 	/** End of oldTerms. */
60 	public static final int RDFTERM_BAG_ID = 12;
61 	/** */
62 	public static final int RDFTERM_FIRST_CORE = RDFTERM_RDF;
63 	/** */
64 	public static final int RDFTERM_LAST_CORE = RDFTERM_DATATYPE;
65 	/** ! Yes, the syntax terms include the core terms. */
66 	public static final int RDFTERM_FIRST_SYNTAX = RDFTERM_FIRST_CORE;
67 	/** */
68 	public static final int RDFTERM_LAST_SYNTAX = RDFTERM_LI;
69 	/** */
70 	public static final int RDFTERM_FIRST_OLD = RDFTERM_ABOUT_EACH;
71 	/** */
72 	public static final int RDFTERM_LAST_OLD = RDFTERM_BAG_ID;
73 
74 	/** this prefix is used for default namespaces */
75 	public static final String DEFAULT_PREFIX = "_dflt";
76 
77 
78 
79 	/**
80 	 * The main parsing method. The XML tree is walked through from the root node and and XMP tree
81 	 * is created. This is a raw parse, the normalisation of the XMP tree happens outside.
82 	 *
83 	 * @param xmlRoot the XML root node
84 	 * @return Returns an XMP metadata object (not normalized)
85 	 * @throws XMPException Occurs if the parsing fails for any reason.
86 	 */
parse(Node xmlRoot)87 	static XMPMetaImpl parse(Node xmlRoot) throws XMPException
88 	{
89 		XMPMetaImpl xmp = new XMPMetaImpl();
90 		rdf_RDF(xmp, xmlRoot);
91 		return xmp;
92 	}
93 
94 
95 	/**
96 	 * Each of these parsing methods is responsible for recognizing an RDF
97 	 * syntax production and adding the appropriate structure to the XMP tree.
98 	 * They simply return for success, failures will throw an exception.
99 	 *
100 	 * @param xmp the xmp metadata object that is generated
101 	 * @param rdfRdfNode the top-level xml node
102 	 * @throws XMPException thown on parsing errors
103 	 */
rdf_RDF(XMPMetaImpl xmp, Node rdfRdfNode)104 	static void rdf_RDF(XMPMetaImpl xmp, Node rdfRdfNode) throws XMPException
105 	{
106 		if (rdfRdfNode.hasAttributes())
107 		{
108 			rdf_NodeElementList (xmp, xmp.getRoot(), rdfRdfNode);
109 		}
110 		else
111 		{
112 			throw new XMPException("Invalid attributes of rdf:RDF element", BADRDF);
113 		}
114 	}
115 
116 
117 	/**
118 	 * 7.2.10 nodeElementList<br>
119 	 * ws* ( nodeElement ws* )*
120 	 *
121 	 * Note: this method is only called from the rdf:RDF-node (top level)
122 	 * @param xmp the xmp metadata object that is generated
123 	 * @param xmpParent the parent xmp node
124 	 * @param rdfRdfNode the top-level xml node
125 	 * @throws XMPException thown on parsing errors
126 	 */
rdf_NodeElementList(XMPMetaImpl xmp, XMPNode xmpParent, Node rdfRdfNode)127 	private static void rdf_NodeElementList(XMPMetaImpl xmp, XMPNode xmpParent, Node rdfRdfNode)
128 		throws XMPException
129 	{
130 		for (int i = 0; i < rdfRdfNode.getChildNodes().getLength(); i++)
131 		{
132 			Node child = rdfRdfNode.getChildNodes().item(i);
133 			// filter whitespaces (and all text nodes)
134 			if (!isWhitespaceNode(child))
135 			{
136 				rdf_NodeElement  (xmp, xmpParent, child, true);
137 			}
138 		}
139 	}
140 
141 
142 	/**
143  	 * 7.2.5 nodeElementURIs
144 	 * 		anyURI - ( coreSyntaxTerms | rdf:li | oldTerms )
145 	 *
146  	 * 7.2.11 nodeElement
147 	 * 		start-element ( URI == nodeElementURIs,
148 	 * 		attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) )
149 	 * 		propertyEltList
150 	 * 		end-element()
151 	 *
152 	 * A node element URI is rdf:Description or anything else that is not an RDF
153 	 * term.
154 	 *
155 	 * @param xmp the xmp metadata object that is generated
156 	 * @param xmpParent the parent xmp node
157 	 * @param xmlNode the currently processed XML node
158 	 * @param isTopLevel Flag if the node is a top-level node
159 	 * @throws XMPException thown on parsing errors
160 	 */
rdf_NodeElement(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, boolean isTopLevel)161 	private static void rdf_NodeElement(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode,
162 			boolean isTopLevel) throws XMPException
163 	{
164 		int nodeTerm = getRDFTermKind (xmlNode);
165 		if (nodeTerm != RDFTERM_DESCRIPTION  &&  nodeTerm != RDFTERM_OTHER)
166 		{
167 			throw new XMPException("Node element must be rdf:Description or typed node",
168 				BADRDF);
169 		}
170 		else if (isTopLevel  &&  nodeTerm == RDFTERM_OTHER)
171 		{
172 			throw new XMPException("Top level typed node not allowed", BADXMP);
173 		}
174 		else
175 		{
176 			rdf_NodeElementAttrs (xmp, xmpParent, xmlNode, isTopLevel);
177 			rdf_PropertyElementList (xmp, xmpParent, xmlNode, isTopLevel);
178 		}
179 
180 	}
181 
182 
183 	/**
184 	 *
185 	 * 7.2.7 propertyAttributeURIs
186 	 * 		anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms )
187 	 *
188 	 * 7.2.11 nodeElement
189 	 * start-element ( URI == nodeElementURIs,
190 	 * 					attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) )
191 	 * 					propertyEltList
192 	 * 					end-element()
193 	 *
194 	 * Process the attribute list for an RDF node element. A property attribute URI is
195 	 * anything other than an RDF term. The rdf:ID and rdf:nodeID attributes are simply ignored,
196 	 * as are rdf:about attributes on inner nodes.
197 	 *
198 	 * @param xmp the xmp metadata object that is generated
199 	 * @param xmpParent the parent xmp node
200 	 * @param xmlNode the currently processed XML node
201 	 * @param isTopLevel Flag if the node is a top-level node
202 	 * @throws XMPException thown on parsing errors
203 	 */
rdf_NodeElementAttrs(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, boolean isTopLevel)204 	private static void rdf_NodeElementAttrs(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode,
205 			boolean isTopLevel) throws XMPException
206 	{
207 		// Used to detect attributes that are mutually exclusive.
208 		int exclusiveAttrs = 0;
209 
210 		for (int i = 0; i < xmlNode.getAttributes().getLength(); i++)
211 		{
212 			Node attribute = xmlNode.getAttributes().item(i);
213 
214 			// quick hack, ns declarations do not appear in C++
215 			// ignore "ID" without namespace
216 			if ("xmlns".equals(attribute.getPrefix())  ||
217 				(attribute.getPrefix() == null  &&  "xmlns".equals(attribute.getNodeName())))
218 			{
219 				continue;
220 			}
221 
222 			int attrTerm = getRDFTermKind(attribute);
223 
224 			switch (attrTerm)
225 			{
226 				case RDFTERM_ID:
227 				case RDFTERM_NODE_ID:
228 				case RDFTERM_ABOUT:
229 					if (exclusiveAttrs > 0)
230 					{
231 						throw new XMPException("Mutally exclusive about, ID, nodeID attributes",
232 								BADRDF);
233 					}
234 
235 					exclusiveAttrs++;
236 
237 					if (isTopLevel && (attrTerm == RDFTERM_ABOUT))
238 					{
239 						// This is the rdf:about attribute on a top level node. Set
240 						// the XMP tree name if
241 						// it doesn't have a name yet. Make sure this name matches
242 						// the XMP tree name.
243 						if (xmpParent.getName() != null && xmpParent.getName().length() > 0)
244 						{
245 							if (!xmpParent.getName().equals(attribute.getNodeValue()))
246 							{
247 								throw new XMPException("Mismatched top level rdf:about values",
248 										BADXMP);
249 							}
250 						}
251 						else
252 						{
253 							xmpParent.setName(attribute.getNodeValue());
254 						}
255 					}
256 					break;
257 
258 				case RDFTERM_OTHER:
259 					addChildNode(xmp, xmpParent, attribute, attribute.getNodeValue(), isTopLevel);
260 					break;
261 
262 				default:
263 					throw new XMPException("Invalid nodeElement attribute", BADRDF);
264 			}
265 
266 		}
267 	}
268 
269 
270 	/**
271 	 * 7.2.13 propertyEltList
272 	 * ws* ( propertyElt ws* )*
273 	 *
274 	 * @param xmp the xmp metadata object that is generated
275 	 * @param xmpParent the parent xmp node
276 	 * @param xmlParent the currently processed XML node
277 	 * @param isTopLevel Flag if the node is a top-level node
278 	 * @throws XMPException thown on parsing errors
279 	 */
rdf_PropertyElementList(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlParent, boolean isTopLevel)280 	private static void rdf_PropertyElementList(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlParent,
281 			boolean isTopLevel) throws XMPException
282 	{
283 		for (int i = 0; i < xmlParent.getChildNodes().getLength(); i++)
284 		{
285 			Node currChild = xmlParent.getChildNodes().item(i);
286 			if (isWhitespaceNode(currChild))
287 			{
288 				continue;
289 			}
290 			else if (currChild.getNodeType() != Node.ELEMENT_NODE)
291 			{
292 				throw new XMPException("Expected property element node not found", BADRDF);
293 			}
294 			else
295 			{
296 				rdf_PropertyElement(xmp, xmpParent, currChild, isTopLevel);
297 			}
298 		}
299 	}
300 
301 
302 	/**
303 	 * 7.2.14 propertyElt
304 	 *
305 	 *		resourcePropertyElt | literalPropertyElt | parseTypeLiteralPropertyElt |
306 	 *		parseTypeResourcePropertyElt | parseTypeCollectionPropertyElt |
307 	 *		parseTypeOtherPropertyElt | emptyPropertyElt
308 	 *
309 	 * 7.2.15 resourcePropertyElt
310 	 *		start-element ( URI == propertyElementURIs, attributes == set ( idAttr? ) )
311 	 *		ws* nodeElement ws*
312 	 *		end-element()
313 	 *
314 	 * 7.2.16 literalPropertyElt
315 	 *		start-element (
316 	 *			URI == propertyElementURIs, attributes == set ( idAttr?, datatypeAttr?) )
317 	 *		text()
318 	 *		end-element()
319 	 *
320 	 * 7.2.17 parseTypeLiteralPropertyElt
321 	 *		start-element (
322 	 *			URI == propertyElementURIs, attributes == set ( idAttr?, parseLiteral ) )
323 	 *		literal
324 	 *		end-element()
325 	 *
326 	 * 7.2.18 parseTypeResourcePropertyElt
327 	 *		start-element (
328 	 *			 URI == propertyElementURIs, attributes == set ( idAttr?, parseResource ) )
329 	 *		propertyEltList
330 	 *		end-element()
331 	 *
332 	 * 7.2.19 parseTypeCollectionPropertyElt
333 	 *		start-element (
334 	 *			URI == propertyElementURIs, attributes == set ( idAttr?, parseCollection ) )
335 	 *		nodeElementList
336 	 *		end-element()
337 	 *
338 	 * 7.2.20 parseTypeOtherPropertyElt
339 	 *		start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseOther ) )
340 	 *		propertyEltList
341 	 *		end-element()
342 	 *
343 	 * 7.2.21 emptyPropertyElt
344 	 *		start-element ( URI == propertyElementURIs,
345 	 *			attributes == set ( idAttr?, ( resourceAttr | nodeIdAttr )?, propertyAttr* ) )
346 	 *		end-element()
347 	 *
348 	 * The various property element forms are not distinguished by the XML element name,
349 	 * but by their attributes for the most part. The exceptions are resourcePropertyElt and
350 	 * literalPropertyElt. They are distinguished by their XML element content.
351 	 *
352 	 * NOTE: The RDF syntax does not explicitly include the xml:lang attribute although it can
353 	 * appear in many of these. We have to allow for it in the attibute counts below.
354 	 *
355 	 * @param xmp the xmp metadata object that is generated
356 	 * @param xmpParent the parent xmp node
357 	 * @param xmlNode the currently processed XML node
358 	 * @param isTopLevel Flag if the node is a top-level node
359 	 * @throws XMPException thown on parsing errors
360 	 */
rdf_PropertyElement(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, boolean isTopLevel)361 	private static void rdf_PropertyElement(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode,
362 			boolean isTopLevel) throws XMPException
363 	{
364 		int nodeTerm = getRDFTermKind (xmlNode);
365 		if (!isPropertyElementName(nodeTerm))
366 		{
367 			throw new XMPException("Invalid property element name", BADRDF);
368 		}
369 
370 		// remove the namespace-definitions from the list
371 		NamedNodeMap attributes = xmlNode.getAttributes();
372 		List nsAttrs = null;
373 		for (int i = 0; i < attributes.getLength(); i++)
374 		{
375 			Node attribute = attributes.item(i);
376 			if ("xmlns".equals(attribute.getPrefix())  ||
377 				(attribute.getPrefix() == null  &&  "xmlns".equals(attribute.getNodeName())))
378 			{
379 				if (nsAttrs == null)
380 				{
381 					nsAttrs = new ArrayList();
382 				}
383 				nsAttrs.add(attribute.getNodeName());
384 			}
385 		}
386 		if (nsAttrs != null)
387 		{
388 			for (Iterator it = nsAttrs.iterator(); it.hasNext();)
389 			{
390 				String ns = (String) it.next();
391 				attributes.removeNamedItem(ns);
392 			}
393 		}
394 
395 
396 		if (attributes.getLength() > 3)
397 		{
398 			// Only an emptyPropertyElt can have more than 3 attributes.
399 			rdf_EmptyPropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
400 		}
401 		else
402 		{
403 			// Look through the attributes for one that isn't rdf:ID or xml:lang,
404 			// it will usually tell what we should be dealing with.
405 			// The called routines must verify their specific syntax!
406 
407 			for (int i = 0; i < attributes.getLength(); i++)
408 			{
409 				Node attribute = attributes.item(i);
410 				String attrLocal = attribute.getLocalName();
411 				String attrNS = attribute.getNamespaceURI();
412 				String attrValue = attribute.getNodeValue();
413 				if (!(XML_LANG.equals(attribute.getNodeName())  &&
414 					!("ID".equals(attrLocal)  &&  NS_RDF.equals(attrNS))))
415 				{
416 					if ("datatype".equals(attrLocal)  &&  NS_RDF.equals(attrNS))
417 					{
418 						rdf_LiteralPropertyElement (xmp, xmpParent, xmlNode, isTopLevel);
419 					}
420 					else if (!("parseType".equals(attrLocal)  &&  NS_RDF.equals(attrNS)))
421 					{
422 						rdf_EmptyPropertyElement (xmp, xmpParent, xmlNode, isTopLevel);
423 					}
424 					else if ("Literal".equals(attrValue))
425 					{
426 						rdf_ParseTypeLiteralPropertyElement();
427 					}
428 					else if ("Resource".equals(attrValue))
429 					{
430 						rdf_ParseTypeResourcePropertyElement(xmp, xmpParent, xmlNode, isTopLevel);
431 					}
432 					else if ("Collection".equals(attrValue))
433 					{
434 						rdf_ParseTypeCollectionPropertyElement();
435 					}
436 					else
437 					{
438 						rdf_ParseTypeOtherPropertyElement();
439 					}
440 
441 					return;
442 				}
443 			}
444 
445 			// Only rdf:ID and xml:lang, could be a resourcePropertyElt, a literalPropertyElt,
446 			// or an emptyPropertyElt. Look at the child XML nodes to decide which.
447 
448 			if (xmlNode.hasChildNodes())
449 			{
450 				for (int i = 0; i < xmlNode.getChildNodes().getLength(); i++)
451 				{
452 					Node currChild = xmlNode.getChildNodes().item(i);
453 					if (currChild.getNodeType() != Node.TEXT_NODE)
454 					{
455 						rdf_ResourcePropertyElement (xmp, xmpParent, xmlNode, isTopLevel);
456 						return;
457 					}
458 				}
459 
460 				rdf_LiteralPropertyElement (xmp, xmpParent, xmlNode, isTopLevel);
461 			}
462 			else
463 			{
464 				rdf_EmptyPropertyElement (xmp, xmpParent, xmlNode, isTopLevel);
465 			}
466 		}
467 	}
468 
469 
470 	/**
471 	 * 7.2.15 resourcePropertyElt
472 	 *		start-element ( URI == propertyElementURIs, attributes == set ( idAttr? ) )
473 	 *		ws* nodeElement ws*
474 	 *		end-element()
475 	 *
476 	 * This handles structs using an rdf:Description node,
477 	 * arrays using rdf:Bag/Seq/Alt, and typedNodes. It also catches and cleans up qualified
478 	 * properties written with rdf:Description and rdf:value.
479 	 *
480 	 * @param xmp the xmp metadata object that is generated
481 	 * @param xmpParent the parent xmp node
482 	 * @param xmlNode the currently processed XML node
483 	 * @param isTopLevel Flag if the node is a top-level node
484 	 * @throws XMPException thown on parsing errors
485 	 */
rdf_ResourcePropertyElement(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, boolean isTopLevel)486 	private static void rdf_ResourcePropertyElement(XMPMetaImpl xmp, XMPNode xmpParent,
487 			Node xmlNode, boolean isTopLevel) throws XMPException
488 	{
489 		if (isTopLevel  &&  "iX:changes".equals(xmlNode.getNodeName()))
490 		{
491 			// Strip old "punchcard" chaff which has on the prefix "iX:".
492 			return;
493 		}
494 
495 		XMPNode newCompound = addChildNode(xmp, xmpParent, xmlNode, "", isTopLevel);
496 
497 		// walk through the attributes
498 		for (int i = 0; i < xmlNode.getAttributes().getLength(); i++)
499 		{
500 			Node attribute = xmlNode.getAttributes().item(i);
501 			if ("xmlns".equals(attribute.getPrefix())  ||
502 					(attribute.getPrefix() == null  &&  "xmlns".equals(attribute.getNodeName())))
503 			{
504 				continue;
505 			}
506 
507 			String attrLocal = attribute.getLocalName();
508 			String attrNS = attribute.getNamespaceURI();
509 			if (XML_LANG.equals(attribute.getNodeName()))
510 			{
511 				addQualifierNode (newCompound, XML_LANG, attribute.getNodeValue());
512 			}
513 			else if ("ID".equals(attrLocal)  &&  NS_RDF.equals(attrNS))
514 			{
515 				continue;	// Ignore all rdf:ID attributes.
516 			}
517 			else
518 			{
519 				throw new XMPException(
520 					"Invalid attribute for resource property element", BADRDF);
521 			}
522 		}
523 
524 		// walk through the children
525 
526 		Node currChild = null;
527 		boolean found = false;
528 		int i;
529 		for (i = 0; i < xmlNode.getChildNodes().getLength(); i++)
530 		{
531 			currChild = xmlNode.getChildNodes().item(i);
532 			if (!isWhitespaceNode(currChild))
533 			{
534 				if (currChild.getNodeType() == Node.ELEMENT_NODE  &&  !found)
535 				{
536 					boolean isRDF = NS_RDF.equals(currChild.getNamespaceURI());
537 					String childLocal = currChild.getLocalName();
538 
539 					if (isRDF  &&  "Bag".equals(childLocal))
540 					{
541 						newCompound.getOptions().setArray(true);
542 					}
543 					else if (isRDF  &&  "Seq".equals(childLocal))
544 					{
545 						newCompound.getOptions().setArray(true).setArrayOrdered(true);
546 					}
547 					else if (isRDF  &&  "Alt".equals(childLocal))
548 					{
549 						newCompound.getOptions().setArray(true).setArrayOrdered(true)
550 								.setArrayAlternate(true);
551 					}
552 					else
553 					{
554 						newCompound.getOptions().setStruct(true);
555 						if (!isRDF  &&  !"Description".equals(childLocal))
556 						{
557 							String typeName = currChild.getNamespaceURI();
558 							if (typeName == null)
559 							{
560 								throw new XMPException(
561 										"All XML elements must be in a namespace", BADXMP);
562 							}
563 							typeName += ':' + childLocal;
564 							addQualifierNode (newCompound, "rdf:type", typeName);
565 						}
566 					}
567 
568 					rdf_NodeElement (xmp, newCompound, currChild, false);
569 
570 					if (newCompound.getHasValueChild())
571 					{
572 						fixupQualifiedNode (newCompound);
573 					}
574 					else if (newCompound.getOptions().isArrayAlternate())
575 					{
576 						XMPNodeUtils.detectAltText(newCompound);
577 					}
578 
579 					found = true;
580 				}
581 				else if (found)
582 				{
583 					// found second child element
584 					throw new XMPException(
585 						"Invalid child of resource property element", BADRDF);
586 				}
587 				else
588 				{
589 					throw new XMPException(
590 						"Children of resource property element must be XML elements", BADRDF);
591 				}
592 			}
593 		}
594 
595 		if (!found)
596 		{
597 			// didn't found any child elements
598 			throw new XMPException("Missing child of resource property element", BADRDF);
599 		}
600 	}
601 
602 
603 	/**
604 	 * 7.2.16 literalPropertyElt
605 	 *		start-element ( URI == propertyElementURIs,
606 	 *				attributes == set ( idAttr?, datatypeAttr?) )
607 	 *		text()
608 	 *		end-element()
609 	 *
610 	 * Add a leaf node with the text value and qualifiers for the attributes.
611 	 * @param xmp the xmp metadata object that is generated
612 	 * @param xmpParent the parent xmp node
613 	 * @param xmlNode the currently processed XML node
614 	 * @param isTopLevel Flag if the node is a top-level node
615 	 * @throws XMPException thown on parsing errors
616 	 */
rdf_LiteralPropertyElement(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, boolean isTopLevel)617 	private static void rdf_LiteralPropertyElement(XMPMetaImpl xmp, XMPNode xmpParent,
618 			Node xmlNode, boolean isTopLevel) throws XMPException
619 	{
620 		XMPNode newChild = addChildNode (xmp, xmpParent, xmlNode, null, isTopLevel);
621 
622 		for (int i = 0; i < xmlNode.getAttributes().getLength(); i++)
623 		{
624 			Node attribute = xmlNode.getAttributes().item(i);
625 			if ("xmlns".equals(attribute.getPrefix())  ||
626 					(attribute.getPrefix() == null  &&  "xmlns".equals(attribute.getNodeName())))
627 			{
628 				continue;
629 			}
630 
631 			String attrNS = attribute.getNamespaceURI();
632 			String attrLocal = attribute.getLocalName();
633 			if (XML_LANG.equals(attribute.getNodeName()))
634 			{
635 				addQualifierNode(newChild, XML_LANG, attribute.getNodeValue());
636 			}
637 			else if (NS_RDF.equals(attrNS)  &&
638 					 ("ID".equals(attrLocal)  ||  "datatype".equals(attrLocal)))
639 			{
640 				continue;	// Ignore all rdf:ID and rdf:datatype attributes.
641 			}
642 			else
643 			{
644 				throw new XMPException(
645 					"Invalid attribute for literal property element", BADRDF);
646 			}
647 		}
648 		String textValue = "";
649 		for (int i = 0; i < xmlNode.getChildNodes().getLength(); i++)
650 		{
651 			Node child = xmlNode.getChildNodes().item(i);
652 			if (child.getNodeType() == Node.TEXT_NODE)
653 			{
654 				textValue += child.getNodeValue();
655 			}
656 			else
657 			{
658 				throw new XMPException("Invalid child of literal property element", BADRDF);
659 			}
660 		}
661 		newChild.setValue(textValue);
662 	}
663 
664 
665 	/**
666 	 * 7.2.17 parseTypeLiteralPropertyElt
667 	 *		start-element ( URI == propertyElementURIs,
668 	 *			attributes == set ( idAttr?, parseLiteral ) )
669 	 *		literal
670 	 *		end-element()
671 	 *
672 	 * @throws XMPException thown on parsing errors
673 	 */
rdf_ParseTypeLiteralPropertyElement()674 	private static void rdf_ParseTypeLiteralPropertyElement() throws XMPException
675 	{
676 		throw new XMPException("ParseTypeLiteral property element not allowed", BADXMP);
677 	}
678 
679 
680 	/**
681 	 * 7.2.18 parseTypeResourcePropertyElt
682 	 *		start-element ( URI == propertyElementURIs,
683 	 *			attributes == set ( idAttr?, parseResource ) )
684 	 *		propertyEltList
685 	 *		end-element()
686 	 *
687 	 * Add a new struct node with a qualifier for the possible rdf:ID attribute.
688 	 * Then process the XML child nodes to get the struct fields.
689 	 *
690 	 * @param xmp the xmp metadata object that is generated
691 	 * @param xmpParent the parent xmp node
692 	 * @param xmlNode the currently processed XML node
693 	 * @param isTopLevel Flag if the node is a top-level node
694 	 * @throws XMPException thown on parsing errors
695 	 */
rdf_ParseTypeResourcePropertyElement(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, boolean isTopLevel)696 	private static void rdf_ParseTypeResourcePropertyElement(XMPMetaImpl xmp, XMPNode xmpParent,
697 			Node xmlNode, boolean isTopLevel) throws XMPException
698 	{
699 		XMPNode newStruct = addChildNode (xmp, xmpParent, xmlNode, "", isTopLevel);
700 
701 		newStruct.getOptions().setStruct(true);
702 
703 		for (int i = 0; i < xmlNode.getAttributes().getLength(); i++)
704 		{
705 			Node attribute = xmlNode.getAttributes().item(i);
706 			if ("xmlns".equals(attribute.getPrefix())  ||
707 					(attribute.getPrefix() == null  &&  "xmlns".equals(attribute.getNodeName())))
708 			{
709 				continue;
710 			}
711 
712 			String attrLocal = attribute.getLocalName();
713 			String attrNS = attribute.getNamespaceURI();
714 			if (XML_LANG.equals(attribute.getNodeName()))
715 			{
716 				addQualifierNode (newStruct, XML_LANG, attribute.getNodeValue());
717 			}
718 			else if (NS_RDF.equals(attrNS)  &&
719 					 ("ID".equals(attrLocal)  ||  "parseType".equals(attrLocal)))
720 			{
721 				continue;	// The caller ensured the value is "Resource".
722 							// Ignore all rdf:ID attributes.
723 			}
724 			else
725 			{
726 				throw new XMPException("Invalid attribute for ParseTypeResource property element",
727 						BADRDF);
728 			}
729 		}
730 
731 		rdf_PropertyElementList (xmp, newStruct, xmlNode, false);
732 
733 		if (newStruct.getHasValueChild())
734 		{
735 			fixupQualifiedNode (newStruct);
736 		}
737 	}
738 
739 
740 	/**
741 	 * 7.2.19 parseTypeCollectionPropertyElt
742 	 *		start-element ( URI == propertyElementURIs,
743 	 *			attributes == set ( idAttr?, parseCollection ) )
744 	 *		nodeElementList
745 	 *		end-element()
746 	 *
747 	 * @throws XMPException thown on parsing errors
748 	 */
rdf_ParseTypeCollectionPropertyElement()749 	private static void rdf_ParseTypeCollectionPropertyElement() throws XMPException
750 	{
751 		throw new XMPException("ParseTypeCollection property element not allowed", BADXMP);
752 	}
753 
754 
755 	/**
756 	 * 7.2.20 parseTypeOtherPropertyElt
757 	 *		start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseOther ) )
758 	 *		propertyEltList
759 	 *		end-element()
760 	 *
761 	 * @throws XMPException thown on parsing errors
762 	 */
rdf_ParseTypeOtherPropertyElement()763 	private static void rdf_ParseTypeOtherPropertyElement() throws XMPException
764 	{
765 		throw new XMPException("ParseTypeOther property element not allowed", BADXMP);
766 	}
767 
768 
769 	/**
770 	 * 7.2.21 emptyPropertyElt
771 	 *		start-element ( URI == propertyElementURIs,
772 	 *						attributes == set (
773 	 *							idAttr?, ( resourceAttr | nodeIdAttr )?, propertyAttr* ) )
774 	 *		end-element()
775 	 *
776 	 *	<ns:Prop1/>  <!-- a simple property with an empty value -->
777 	 *	<ns:Prop2 rdf:resource="http: *www.adobe.com/"/> <!-- a URI value -->
778 	 *	<ns:Prop3 rdf:value="..." ns:Qual="..."/> <!-- a simple qualified property -->
779 	 *	<ns:Prop4 ns:Field1="..." ns:Field2="..."/> <!-- a struct with simple fields -->
780 	 *
781 	 * An emptyPropertyElt is an element with no contained content, just a possibly empty set of
782 	 * attributes. An emptyPropertyElt can represent three special cases of simple XMP properties: a
783 	 * simple property with an empty value (ns:Prop1), a simple property whose value is a URI
784 	 * (ns:Prop2), or a simple property with simple qualifiers (ns:Prop3).
785 	 * An emptyPropertyElt can also represent an XMP struct whose fields are all simple and
786 	 * unqualified (ns:Prop4).
787 	 *
788 	 * It is an error to use both rdf:value and rdf:resource - that can lead to invalid  RDF in the
789 	 * verbose form written using a literalPropertyElt.
790 	 *
791 	 * The XMP mapping for an emptyPropertyElt is a bit different from generic RDF, partly for
792 	 * design reasons and partly for historical reasons. The XMP mapping rules are:
793 	 * <ol>
794 	 *		<li> If there is an rdf:value attribute then this is a simple property
795 	 *				 with a text value.
796 	 *		All other attributes are qualifiers.
797 	 *		<li> If there is an rdf:resource attribute then this is a simple property
798 	 *			with a URI value.
799 	 *		All other attributes are qualifiers.
800 	 *		<li> If there are no attributes other than xml:lang, rdf:ID, or rdf:nodeID
801 	 *				then this is a simple
802 	 *		property with an empty value.
803 	 *		<li> Otherwise this is a struct, the attributes other than xml:lang, rdf:ID,
804 	 *				or rdf:nodeID are fields.
805 	 * </ol>
806 	 *
807 	 * @param xmp the xmp metadata object that is generated
808 	 * @param xmpParent the parent xmp node
809 	 * @param xmlNode the currently processed XML node
810 	 * @param isTopLevel Flag if the node is a top-level node
811 	 * @throws XMPException thown on parsing errors
812 	 */
rdf_EmptyPropertyElement(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, boolean isTopLevel)813 	private static void rdf_EmptyPropertyElement(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode,
814 			boolean isTopLevel) throws XMPException
815 	{
816 		boolean hasPropertyAttrs = false;
817 		boolean hasResourceAttr = false;
818 		boolean hasNodeIDAttr = false;
819 		boolean hasValueAttr = false;
820 
821 		Node valueNode = null;	// ! Can come from rdf:value or rdf:resource.
822 
823 		if (xmlNode.hasChildNodes())
824 		{
825 			throw new XMPException(
826 					"Nested content not allowed with rdf:resource or property attributes",
827 					BADRDF);
828 		}
829 
830 		// First figure out what XMP this maps to and remember the XML node for a simple value.
831 		for (int i = 0; i < xmlNode.getAttributes().getLength(); i++)
832 		{
833 			Node attribute = xmlNode.getAttributes().item(i);
834 			if ("xmlns".equals(attribute.getPrefix())  ||
835 					(attribute.getPrefix() == null  &&  "xmlns".equals(attribute.getNodeName())))
836 			{
837 				continue;
838 			}
839 
840 			int attrTerm = getRDFTermKind (attribute);
841 
842 			switch (attrTerm)
843 			{
844 				case RDFTERM_ID :
845 					// Nothing to do.
846 					break;
847 
848 				case RDFTERM_RESOURCE :
849 					if (hasNodeIDAttr)
850 					{
851 						throw new XMPException(
852 							"Empty property element can't have both rdf:resource and rdf:nodeID",
853 							BADRDF);
854 					}
855 					else if (hasValueAttr)
856 					{
857 						throw new XMPException(
858 								"Empty property element can't have both rdf:value and rdf:resource",
859 								BADXMP);
860 					}
861 
862 					hasResourceAttr = true;
863 					if (!hasValueAttr)
864 					{
865 						valueNode = attribute;
866 					}
867 					break;
868 
869 				case RDFTERM_NODE_ID:
870 				if (hasResourceAttr)
871 				{
872 					throw new XMPException(
873 							"Empty property element can't have both rdf:resource and rdf:nodeID",
874 							BADRDF);
875 				}
876 				hasNodeIDAttr = true;
877 				break;
878 
879 			case RDFTERM_OTHER:
880 				if ("value".equals(attribute.getLocalName())
881 						&& NS_RDF.equals(attribute.getNamespaceURI()))
882 				{
883 					if (hasResourceAttr)
884 					{
885 						throw new XMPException(
886 								"Empty property element can't have both rdf:value and rdf:resource",
887 								BADXMP);
888 					}
889 					hasValueAttr = true;
890 					valueNode = attribute;
891 				}
892 				else if (!XML_LANG.equals(attribute.getNodeName()))
893 				{
894 					hasPropertyAttrs = true;
895 				}
896 				break;
897 
898 			default:
899 				throw new XMPException("Unrecognized attribute of empty property element",
900 						BADRDF);
901 			}
902 		}
903 
904 		// Create the right kind of child node and visit the attributes again
905 		// to add the fields or qualifiers.
906 		// ! Because of implementation vagaries,
907 		//   the xmpParent is the tree root for top level properties.
908 		// ! The schema is found, created if necessary, by addChildNode.
909 
910 		XMPNode childNode = addChildNode(xmp, xmpParent, xmlNode, "", isTopLevel);
911 		boolean childIsStruct = false;
912 
913 		if (hasValueAttr || hasResourceAttr)
914 		{
915 			childNode.setValue(valueNode != null ? valueNode.getNodeValue() : "");
916 			if (!hasValueAttr)
917 			{
918 				// ! Might have both rdf:value and rdf:resource.
919 				childNode.getOptions().setURI(true);
920 			}
921 		}
922 		else if (hasPropertyAttrs)
923 		{
924 			childNode.getOptions().setStruct(true);
925 			childIsStruct = true;
926 		}
927 
928 		for (int i = 0; i < xmlNode.getAttributes().getLength(); i++)
929 		{
930 			Node attribute = xmlNode.getAttributes().item(i);
931 			if (attribute == valueNode  ||
932 				"xmlns".equals(attribute.getPrefix())  ||
933 				(attribute.getPrefix() == null  &&  "xmlns".equals(attribute.getNodeName())))
934 			{
935 				continue;	// Skip the rdf:value or rdf:resource attribute holding the value.
936 			}
937 
938 			int attrTerm = getRDFTermKind (attribute);
939 
940 			switch (attrTerm)
941 			{
942 				case RDFTERM_ID :
943 				case RDFTERM_NODE_ID :
944 					break;	// Ignore all rdf:ID and rdf:nodeID attributes.
945 
946 				case RDFTERM_RESOURCE :
947 					addQualifierNode(childNode, "rdf:resource", attribute.getNodeValue());
948 					break;
949 
950 				case RDFTERM_OTHER :
951 					if (!childIsStruct)
952 					{
953 						addQualifierNode(
954 							childNode, attribute.getNodeName(), attribute.getNodeValue());
955 					}
956 					else if (XML_LANG.equals(attribute.getNodeName()))
957 					{
958 						addQualifierNode (childNode, XML_LANG, attribute.getNodeValue());
959 					}
960 					else
961 					{
962 						addChildNode (xmp, childNode, attribute, attribute.getNodeValue(), false);
963 					}
964 					break;
965 
966 				default :
967 					throw new XMPException("Unrecognized attribute of empty property element",
968 						BADRDF);
969 			}
970 
971 		}
972 	}
973 
974 
975 	/**
976 	 * Adds a child node.
977 	 *
978 	 * @param xmp the xmp metadata object that is generated
979 	 * @param xmpParent the parent xmp node
980 	 * @param xmlNode the currently processed XML node
981 	 * @param value Node value
982 	 * @param isTopLevel Flag if the node is a top-level node
983 	 * @return Returns the newly created child node.
984 	 * @throws XMPException thown on parsing errors
985 	 */
addChildNode(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode, String value, boolean isTopLevel)986 	private static XMPNode addChildNode(XMPMetaImpl xmp, XMPNode xmpParent, Node xmlNode,
987 			String value, boolean isTopLevel) throws XMPException
988 	{
989 		XMPSchemaRegistry registry = XMPMetaFactory.getSchemaRegistry();
990 		String namespace = xmlNode.getNamespaceURI();
991 		String childName;
992 		if (namespace != null)
993 		{
994 			if (NS_DC_DEPRECATED.equals(namespace))
995 			{
996 				// Fix a legacy DC namespace
997 				namespace = NS_DC;
998 			}
999 
1000 			String prefix = registry.getNamespacePrefix(namespace);
1001 			if (prefix == null)
1002 			{
1003 				prefix = xmlNode.getPrefix() != null ? xmlNode.getPrefix() : DEFAULT_PREFIX;
1004 				prefix = registry.registerNamespace(namespace, prefix);
1005 			}
1006 			childName = prefix + xmlNode.getLocalName();
1007 		}
1008 		else
1009 		{
1010 			throw new XMPException(
1011 				"XML namespace required for all elements and attributes", BADRDF);
1012 		}
1013 
1014 
1015 		// create schema node if not already there
1016 		PropertyOptions childOptions = new PropertyOptions();
1017 		boolean isAlias = false;
1018 		if (isTopLevel)
1019 		{
1020 			// Lookup the schema node, adjust the XMP parent pointer.
1021 			// Incoming parent must be the tree root.
1022 			XMPNode schemaNode = XMPNodeUtils.findSchemaNode(xmp.getRoot(), namespace,
1023 				DEFAULT_PREFIX, true);
1024 			schemaNode.setImplicit(false);	// Clear the implicit node bit.
1025 			// need runtime check for proper 32 bit code.
1026 			xmpParent = schemaNode;
1027 
1028 			// If this is an alias set the alias flag in the node
1029 			// and the hasAliases flag in the tree.
1030 			if (registry.findAlias(childName) != null)
1031 			{
1032 				isAlias = true;
1033 				xmp.getRoot().setHasAliases(true);
1034 				schemaNode.setHasAliases(true);
1035 			}
1036 		}
1037 
1038 
1039 		// Make sure that this is not a duplicate of a named node.
1040 		boolean isArrayItem  = "rdf:li".equals(childName);
1041 		boolean isValueNode  = "rdf:value".equals(childName);
1042 
1043 		// Create XMP node and so some checks
1044 		XMPNode newChild = new XMPNode(
1045 			childName, value, childOptions);
1046 		newChild.setAlias(isAlias);
1047 
1048 		// Add the new child to the XMP parent node, a value node first.
1049 		if (!isValueNode)
1050 		{
1051 			xmpParent.addChild(newChild);
1052 		}
1053 		else
1054 		{
1055 			xmpParent.addChild(1, newChild);
1056 		}
1057 
1058 
1059 		if (isValueNode)
1060 		{
1061 			if (isTopLevel  ||  !xmpParent.getOptions().isStruct())
1062 			{
1063 				throw new XMPException("Misplaced rdf:value element", BADRDF);
1064 			}
1065 			xmpParent.setHasValueChild(true);
1066 		}
1067 
1068 		if (isArrayItem)
1069 		{
1070 			if (!xmpParent.getOptions().isArray())
1071 			{
1072 				throw new XMPException("Misplaced rdf:li element", BADRDF);
1073 			}
1074 			newChild.setName(ARRAY_ITEM_NAME);
1075 		}
1076 
1077 		return newChild;
1078 	}
1079 
1080 
1081 	/**
1082 	 * Adds a qualifier node.
1083 	 *
1084 	 * @param xmpParent the parent xmp node
1085 	 * @param name the name of the qualifier which has to be
1086 	 * 		QName including the <b>default prefix</b>
1087 	 * @param value the value of the qualifier
1088 	 * @return Returns the newly created child node.
1089 	 * @throws XMPException thown on parsing errors
1090 	 */
addQualifierNode(XMPNode xmpParent, String name, String value)1091 	private static XMPNode addQualifierNode(XMPNode xmpParent, String name, String value)
1092 			throws XMPException
1093 	{
1094 		boolean isLang = XML_LANG.equals(name);
1095 
1096 		XMPNode newQual = null;
1097 
1098 		// normalize value of language qualifiers
1099 		newQual = new XMPNode(name, isLang ? Utils.normalizeLangValue(value) : value, null);
1100 		xmpParent.addQualifier(newQual);
1101 
1102 		return newQual;
1103 	}
1104 
1105 
1106 	/**
1107 	 * The parent is an RDF pseudo-struct containing an rdf:value field. Fix the
1108 	 * XMP data model. The rdf:value node must be the first child, the other
1109 	 * children are qualifiers. The form, value, and children of the rdf:value
1110 	 * node are the real ones. The rdf:value node's qualifiers must be added to
1111 	 * the others.
1112 	 *
1113 	 * @param xmpParent the parent xmp node
1114 	 * @throws XMPException thown on parsing errors
1115 	 */
fixupQualifiedNode(XMPNode xmpParent)1116 	private static void fixupQualifiedNode(XMPNode xmpParent) throws XMPException
1117 	{
1118 		assert xmpParent.getOptions().isStruct()  &&  xmpParent.hasChildren();
1119 
1120 		XMPNode valueNode = xmpParent.getChild(1);
1121 		assert "rdf:value".equals(valueNode.getName());
1122 
1123 		// Move the qualifiers on the value node to the parent.
1124 		// Make sure an xml:lang qualifier stays at the front.
1125 		// Check for duplicate names between the value node's qualifiers and the parent's children.
1126 		// The parent's children are about to become qualifiers. Check here, between the groups.
1127 		// Intra-group duplicates are caught by XMPNode#addChild(...).
1128 		if (valueNode.getOptions().getHasLanguage())
1129 		{
1130 			if (xmpParent.getOptions().getHasLanguage())
1131 			{
1132 				throw new XMPException("Redundant xml:lang for rdf:value element",
1133 					BADXMP);
1134 			}
1135 			XMPNode langQual = valueNode.getQualifier(1);
1136 			valueNode.removeQualifier(langQual);
1137 			xmpParent.addQualifier(langQual);
1138 		}
1139 
1140 		// Start the remaining copy after the xml:lang qualifier.
1141 		for (int i = 1; i <= valueNode.getQualifierLength(); i++)
1142 		{
1143 			XMPNode qualifier = valueNode.getQualifier(i);
1144 			xmpParent.addQualifier(qualifier);
1145 		}
1146 
1147 
1148 		// Change the parent's other children into qualifiers.
1149 		// This loop starts at 1, child 0 is the rdf:value node.
1150 		for (int i = 2; i <= xmpParent.getChildrenLength(); i++)
1151 		{
1152 			XMPNode qualifier = xmpParent.getChild(i);
1153 			xmpParent.addQualifier(qualifier);
1154 		}
1155 
1156 		// Move the options and value last, other checks need the parent's original options.
1157 		// Move the value node's children to be the parent's children.
1158 		assert xmpParent.getOptions().isStruct()  ||  xmpParent.getHasValueChild();
1159 
1160 		xmpParent.setHasValueChild(false);
1161 		xmpParent.getOptions().setStruct(false);
1162 		xmpParent.getOptions().mergeWith(valueNode.getOptions());
1163 		xmpParent.setValue(valueNode.getValue());
1164 
1165 		xmpParent.removeChildren();
1166 		for (Iterator it = valueNode.iterateChildren(); it.hasNext();)
1167 		{
1168 			XMPNode child = (XMPNode) it.next();
1169 			xmpParent.addChild(child);
1170 		}
1171 	}
1172 
1173 
1174 	/**
1175 	 * Checks if the node is a white space.
1176 	 * @param node an XML-node
1177 	 * @return Returns whether the node is a whitespace node,
1178 	 * 		i.e. a text node that contains only whitespaces.
1179 	 */
isWhitespaceNode(Node node)1180 	private static boolean isWhitespaceNode(Node node)
1181 	{
1182 		if (node.getNodeType() != Node.TEXT_NODE)
1183 		{
1184 			return false;
1185 		}
1186 
1187 		String value = node.getNodeValue();
1188 		for (int i = 0; i < value.length(); i++)
1189 		{
1190 			if (!Character.isWhitespace(value.charAt(i)))
1191 			{
1192 				return false;
1193 			}
1194 		}
1195 
1196 		return true;
1197 	}
1198 
1199 
1200 	/**
1201 	 * 7.2.6 propertyElementURIs
1202 	 *			anyURI - ( coreSyntaxTerms | rdf:Description | oldTerms )
1203 	 *
1204 	 * @param term the term id
1205 	 * @return Return true if the term is a property element name.
1206 	 */
isPropertyElementName(int term)1207 	private static boolean isPropertyElementName(int term)
1208 	{
1209 		if (term == RDFTERM_DESCRIPTION  ||  isOldTerm(term))
1210 		{
1211 			return false;
1212 		}
1213 		else
1214 		{
1215 			return (!isCoreSyntaxTerm(term));
1216 		}
1217 	}
1218 
1219 
1220 	/**
1221 	 * 7.2.4 oldTerms<br>
1222 	 * rdf:aboutEach | rdf:aboutEachPrefix | rdf:bagID
1223 	 *
1224 	 * @param term the term id
1225 	 * @return Returns true if the term is an old term.
1226 	 */
isOldTerm(int term)1227 	private static boolean isOldTerm(int term)
1228 	{
1229 		return  RDFTERM_FIRST_OLD <= term  &&  term <= RDFTERM_LAST_OLD;
1230 	}
1231 
1232 
1233 	/**
1234 	 * 7.2.2 coreSyntaxTerms<br>
1235 	 * rdf:RDF | rdf:ID | rdf:about | rdf:parseType | rdf:resource | rdf:nodeID |
1236 	 * rdf:datatype
1237 	 *
1238 	 * @param term the term id
1239 	 * @return Return true if the term is a core syntax term
1240 	 */
isCoreSyntaxTerm(int term)1241 	private static boolean isCoreSyntaxTerm(int term)
1242 	{
1243 		return  RDFTERM_FIRST_CORE <= term  &&  term <= RDFTERM_LAST_CORE;
1244 	}
1245 
1246 
1247 	/**
1248 	 * Determines the ID for a certain RDF Term.
1249 	 * Arranged to hopefully minimize the parse time for large XMP.
1250 	 *
1251 	 * @param node an XML node
1252 	 * @return Returns the term ID.
1253 	 */
getRDFTermKind(Node node)1254 	private static int getRDFTermKind(Node node)
1255 	{
1256 		String localName = node.getLocalName();
1257 		String namespace = node.getNamespaceURI();
1258 
1259 		if (
1260 				namespace == null  &&
1261 				("about".equals(localName) || "ID".equals(localName))  &&
1262 				(node instanceof Attr)  &&
1263 				NS_RDF.equals(((Attr) node).getOwnerElement().getNamespaceURI())
1264 		   )
1265 		{
1266 			namespace = NS_RDF;
1267 		}
1268 
1269 		if (NS_RDF.equals(namespace))
1270 		{
1271 			if ("li".equals(localName))
1272 			{
1273 				return RDFTERM_LI;
1274 			}
1275 			else if ("parseType".equals(localName))
1276 			{
1277 				return RDFTERM_PARSE_TYPE;
1278 			}
1279 			else if ("Description".equals(localName))
1280 			{
1281 				return RDFTERM_DESCRIPTION;
1282 			}
1283 			else if ("about".equals(localName))
1284 			{
1285 				return RDFTERM_ABOUT;
1286 			}
1287 			else if ("resource".equals(localName))
1288 			{
1289 				return RDFTERM_RESOURCE;
1290 			}
1291 			else if ("RDF".equals(localName))
1292 			{
1293 				return RDFTERM_RDF;
1294 			}
1295 			else if ("ID".equals(localName))
1296 			{
1297 				return RDFTERM_ID;
1298 			}
1299 			else if ("nodeID".equals(localName))
1300 			{
1301 				return RDFTERM_NODE_ID;
1302 			}
1303 			else if ("datatype".equals(localName))
1304 			{
1305 				return RDFTERM_DATATYPE;
1306 			}
1307 			else if ("aboutEach".equals(localName))
1308 			{
1309 				return RDFTERM_ABOUT_EACH;
1310 			}
1311 			else if ("aboutEachPrefix".equals(localName))
1312 			{
1313 				return RDFTERM_ABOUT_EACH_PREFIX;
1314 			}
1315 			else if ("bagID".equals(localName))
1316 			{
1317 				return RDFTERM_BAG_ID;
1318 			}
1319 		}
1320 
1321 		return RDFTERM_OTHER;
1322 	}
1323 }