1
/* Copyright 2002-2005 Elliotte Rusty Harold
3
This library is free software; you can redistribute it and/or modify
4
it under the terms of version 2.1 of the GNU Lesser General Public
5
License as published by the Free Software Foundation.
7
This library is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU Lesser General Public License for more details.
12
You should have received a copy of the GNU Lesser General Public
13
License along with this library; if not, write to the
14
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
15
Boston, MA 02111-1307 USA
17
You can contact Elliotte Rusty Harold by sending e-mail to
18
elharo@metalab.unc.edu. Please include the word "XOM" in the
19
subject line. The XOM home page is located at http://www.xom.nu/
24
package nu.xom.xinclude;
26
import java.util.ArrayList;
27
import java.util.List;
29
import nu.xom.Attribute;
30
import nu.xom.Document;
31
import nu.xom.Element;
32
import nu.xom.IllegalNameException;
35
import nu.xom.ParentNode;
36
import nu.xom.XMLException;
41
* Right now this is just for XInclude, and hence is non-public.
42
* Once it's more baked it will probably become public and move
43
* to a package of its own.
46
* @author Elliotte Rusty Harold
53
// prevent instantiation
57
static Nodes query(Document doc, String xptr)
58
throws XPointerSyntaxException, XPointerResourceException {
60
Nodes result = new Nodes();
61
boolean found = false;
63
try { // Is this a shorthand XPointer?
64
// Need to include a URI in case this is a colonized scheme name
65
new Element(xptr, "http://www.example.com");
66
Element identified = findByID(doc.getRootElement(), xptr);
67
if (identified != null) {
68
result.append(identified);
72
catch (IllegalNameException ex) {
73
// not a bare name; try element() scheme
74
List elementSchemeData = findElementSchemeData(xptr);
75
if (elementSchemeData.size() == 0) {
76
// This may be a legal XPointer, but it doesn't
77
// have an element() scheme so we can't handle it.
78
throw new XPointerSyntaxException(
79
"No supported XPointer schemes found"
83
for (int i = 0; i < elementSchemeData.size(); i++) {
84
String currentData = (String) (elementSchemeData.get(i));
85
int[] keys = new int[0];
86
ParentNode current = doc;
87
if (currentData.indexOf('/') == -1) {
88
// raw id in element like element(f2)
90
new Element(currentData);
92
catch (IllegalNameException inex) {
93
// not a bare name; and doesn't contain a /
94
// This doesn't adhere to the element scheme.
95
// Therefore, according to the XPointer element
96
// scheme spec, " if scheme data in a pointer
97
// part with the element() scheme does not
98
// conform to the syntax defined in this
99
// section the pointer part does not identify
103
Element identified = findByID(
104
doc.getRootElement(), currentData);
105
if (identified != null) {
106
if (!found) result.append(identified);
110
else if (!currentData.startsWith("/")) {
111
String id = currentData.substring(
112
0, currentData.indexOf('/'));
113
// Check to make sure this is a legal
118
catch (XMLException inex) {
119
// doesn't adhere to the element scheme spec;
120
// Therefore this pointer part does not identify
121
// a subresource (See 2nd paragraph of section
122
// 3 of http://www.w3.org/TR/xptr-element/ )
123
// This is not a resource error unless no
124
// XPointer part identifies a subresource.
127
current = findByID(doc.getRootElement(), id);
128
keys = split(currentData.substring(
129
currentData.indexOf('/')));
131
if (current == null) continue;
134
keys = split(currentData);
137
for (int j = 0; j < keys.length; j++) {
138
current = findNthChildElement(current, keys[j]);
139
if (current == null) break;
142
if (current != doc && current != null) {
143
if (!found) result.append(current);
151
if (found) return result;
153
// If we get here and still haven't been able to match an
154
// element, the XPointer has failed.
155
throw new XPointerResourceException(
157
+ " did not locate any nodes in the document "
165
private static Element findNthChildElement(
166
ParentNode parent, int position) {
167
// watch out for 1-based indexing of tumblers
168
int elementCount = 1;
169
for (int i = 0; i < parent.getChildCount(); i++) {
170
Node child = parent.getChild(i);
171
if (child instanceof Element) {
172
if (elementCount == position) return (Element) child;
180
private static int[] split(String tumbler)
181
throws XPointerSyntaxException {
183
int numberOfParts = 0;
184
for (int i = 0; i < tumbler.length(); i++) {
185
if (tumbler.charAt(i) == '/') numberOfParts++;
188
int[] result = new int[numberOfParts];
190
StringBuffer part = new StringBuffer(3);
192
for (int i = 1; i < tumbler.length(); i++) {
193
if (tumbler.charAt(i) == '/') {
194
result[index] = Integer.parseInt(part.toString());
196
part = new StringBuffer(3);
199
part.append(tumbler.charAt(i));
202
result[result.length-1] = Integer.parseInt(part.toString());
204
catch (NumberFormatException ex) {
205
XPointerSyntaxException ex2
206
= new XPointerSyntaxException(tumbler
207
+ " is not syntactically correct", ex);
214
private static List findElementSchemeData(String xpointer)
215
throws XPointerSyntaxException {
217
List result = new ArrayList(1);
219
StringBuffer xptr = new StringBuffer(xpointer.trim());
220
StringBuffer scheme = new StringBuffer();
222
while (i < xptr.length()) {
223
char c = xptr.charAt(i);
225
else scheme.append(c);
229
// need to verify that scheme is a QName
231
// ugly hack because Verifier isn't public
232
new Element(scheme.toString(), "http://www.example.com/");
234
catch (IllegalNameException ex) {
235
throw new XPointerSyntaxException(ex.getMessage());
238
int open = 1; // parentheses count
240
StringBuffer schemeData = new StringBuffer();
243
char c = xptr.charAt(i);
245
c = xptr.charAt(i+1);
246
schemeData.append(c);
247
if (c != '^' && c != '(' && c != ')') {
248
throw new XPointerSyntaxException(
249
"Illegal XPointer escape sequence"
255
schemeData.append(c);
260
if (open > 0) schemeData.append(c);
263
schemeData.append(c);
268
catch (StringIndexOutOfBoundsException ex) {
269
throw new XPointerSyntaxException("Unbalanced parentheses");
272
if (scheme.toString().equals("element")) {
273
result.add(schemeData.toString());
276
if (i + 1 < xptr.length()) {
277
result.addAll(findElementSchemeData(xptr.substring(i)));
284
static Element findByID(Element element, String id) {
286
Node current = element;
291
if (current instanceof Element) {
292
Element currentElement = (Element) current;
293
for (int i = 0; i < currentElement.getAttributeCount(); i++) {
294
Attribute att = currentElement.getAttribute(i);
295
if (att.getType() == Attribute.Type.ID) {
296
if (att.getValue().trim().equals(id)) {
297
return currentElement;
303
if (!end && current.getChildCount() > 0) {
304
current = current.getChild(0);
309
if (current == element) break;
312
ParentNode parent = current.getParent();
313
if (parent.getChildCount() - 1 == index) {
315
if (current != element) {
316
parent = current.getParent();
317
index = parent.indexOf(current);
323
current = parent.getChild(index);