XPointerElementLocator
Implementation
XPointerElementLocator
is an implementation of the abstract class ro.sync.ecss.extensions.api.link.ElementLocator
for links that
have one of the following XPointer element() scheme patterns:
- element (elementID)
-
Locate the element with the specified id.
- element (/1/2/3)
-
A child sequence appearing alone identifies an element by means of stepwise navigation, which is directed by a sequence of integers separated by slashes (/). Each integer n locates the nth child element of the previously located element.
- element (elementID/3/4)
-
A child sequence appearing after a NCName identifies an element by means of stepwise navigation, starting from the element located by the given name.
The constructor separates the id/integers, which are delimited by slashes(/) into a sequence of identifiers (an XPointer path). It will also check that the link has one of the supported patterns of the XPointer element() scheme.
public XPointerElementLocator(IDTypeVerifier idVerifier, String link) throws ElementLocatorException { super(link); this.idVerifier = idVerifier; link = link.substring("element(".length(), link.length() - 1); StringTokenizer stringTokenizer = new StringTokenizer(link, "/", false); xpointerPath = new String[stringTokenizer.countTokens()]; int i = 0; while (stringTokenizer.hasMoreTokens()) { xpointerPath[i] = stringTokenizer.nextToken(); boolean invalidFormat = false; // Empty xpointer component is not supported if(xpointerPath[i].length() == 0){ invalidFormat = true; } if(i > 0){ try { Integer.parseInt(xpointerPath[i]); } catch (NumberFormatException e) { invalidFormat = true; } } if(invalidFormat){ throw new ElementLocatorException( "Only the element() scheme is supported when locating XPointer links." + "Supported formats: element(elementID), element(/1/2/3), element(elemID/2/3/4)."); } i++; } if(Character.isDigit(xpointerPath[0].charAt(0))){ // This is the case when xpointer have the following pattern /1/5/7 xpointerPathDepth = xpointerPath.length; } else { // This is the case when xpointer starts with an element ID xpointerPathDepth = -1; startWithElementID = true; } }
The method startElement
will be invoked at the beginning of every element
in the XML document(even when the element is empty). The arguments it takes are
- uri
-
The namespace URI, or the empty string if the element has no namespace URI or if namespace processing is disabled.
- localName
-
Local name of the element.
- qName
-
Qualified name of the element.
- atts
-
Attributes attached to the element. If there are no attributes, this argument will be empty.
The method returns true
if the processed element is found to be the one
indicated by the link.
The XPointerElementLocator
implementation of the
startElement
will update the depth of the current element and keep the
index of the element in its parent. If the xpointerPath
starts with an
element ID then the current element ID is verified to match the specified ID. If this
is the
case the depth of the XPointer is updated taking into account the depth of the current
element.
If the XPointer path depth is the same as the current element depth then the kept indices of the current element path are compared to the indices in the XPointer path. If all of them match then the element has been found.
public boolean startElement(String uri, String localName, String name, Attr[] atts) { boolean linkLocated = false; // Increase current element document depth startElementDepth ++; if (endElementDepth != startElementDepth) { // The current element is the first child of the parent currentElementIndexStack.push(new Integer(1)); } else { // Another element in the parent element currentElementIndexStack.push(new Integer(lastIndexInParent + 1)); } if (startWithElementID) { // This the case when xpointer path starts with an element ID. String xpointerElement = xpointerPath[0]; for (int i = 0; i < atts.length; i++) { if(xpointerElement.equals(atts[i].getValue())){ if(idVerifier.hasIDType( localName, uri, atts[i].getQName(), atts[i].getNamespace())){ xpointerPathDepth = startElementDepth + xpointerPath.length - 1; break; } } } } if (xpointerPathDepth == startElementDepth){ // check if xpointer path matches with the current element path linkLocated = true; try { int xpointerIdx = xpointerPath.length - 1; int stackIdx = currentElementIndexStack.size() - 1; int stopIdx = startWithElementID ? 1 : 0; while (xpointerIdx >= stopIdx && stackIdx >= 0) { int xpointerIndex = Integer.parseInt(xpointerPath[xpointerIdx]); int currentElementIndex = ((Integer)currentElementIndexStack.get(stackIdx)).intValue(); if(xpointerIndex != currentElementIndex) { linkLocated = false; break; } xpointerIdx--; stackIdx--; } } catch (NumberFormatException e) { logger.warn(e,e); } } return linkLocated; }
The method endElement
will be invoked at the end of every element in the
XML document (even when the element is empty).
The XPointerElementLocator
implementation of the
endElement
updates the depth of the current element path and the index of
the element in its parent.
public void endElement(String uri, String localName, String name) { endElementDepth = startElementDepth; startElementDepth --; lastIndexInParent = ((Integer)currentElementIndexStack.pop()).intValue(); }