~ubuntu-branches/ubuntu/precise/xom/precise

« back to all changes in this revision

Viewing changes to src/nu/xom/xinclude/XPointer.java

  • Committer: Bazaar Package Importer
  • Author(s): Varun Hiremath
  • Date: 2007-11-25 15:50:40 UTC
  • Revision ID: james.westby@ubuntu.com-20071125155040-r75ikcqf1vu0cei7
Tags: upstream-1.1
ImportĀ upstreamĀ versionĀ 1.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright 2002-2005 Elliotte Rusty Harold
 
2
   
 
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.
 
6
   
 
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.
 
11
   
 
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
 
16
   
 
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/
 
20
*/
 
21
 
 
22
 
 
23
 
 
24
package nu.xom.xinclude;
 
25
 
 
26
import java.util.ArrayList;
 
27
import java.util.List;
 
28
 
 
29
import nu.xom.Attribute;
 
30
import nu.xom.Document;
 
31
import nu.xom.Element;
 
32
import nu.xom.IllegalNameException;
 
33
import nu.xom.Node;
 
34
import nu.xom.Nodes;
 
35
import nu.xom.ParentNode;
 
36
import nu.xom.XMLException;
 
37
 
 
38
/**
 
39
 * 
 
40
 * <p>
 
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.
 
44
 * </p>
 
45
 * 
 
46
 * @author Elliotte Rusty Harold
 
47
 * @version 1.1b6
 
48
 *
 
49
 */
 
50
class XPointer {
 
51
    
 
52
    
 
53
    // prevent instantiation
 
54
    private XPointer() {}
 
55
    
 
56
    
 
57
    static Nodes query(Document doc, String xptr) 
 
58
      throws XPointerSyntaxException, XPointerResourceException {    
 
59
            
 
60
        Nodes result = new Nodes();
 
61
        boolean found = false;
 
62
        
 
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);   
 
69
                return result;
 
70
            }
 
71
        }
 
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"
 
80
                );    
 
81
            }
 
82
        
 
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)
 
89
                    try {
 
90
                        new Element(currentData);
 
91
                    }
 
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 
 
100
                        // a subresource."
 
101
                        continue; 
 
102
                    }  
 
103
                    Element identified = findByID(
 
104
                      doc.getRootElement(), currentData); 
 
105
                    if (identified != null) {
 
106
                        if (!found) result.append(identified); 
 
107
                        found = true;                
 
108
                    }
 
109
                }
 
110
                else if (!currentData.startsWith("/")) {
 
111
                    String id = currentData.substring(
 
112
                      0, currentData.indexOf('/'));
 
113
                    // Check to make sure this is a legal 
 
114
                    // XML name/ID value
 
115
                    try {
 
116
                        new Element(id);   
 
117
                    }
 
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.
 
125
                        continue;
 
126
                    }
 
127
                    current = findByID(doc.getRootElement(), id);                         
 
128
                    keys = split(currentData.substring(
 
129
                      currentData.indexOf('/')));
 
130
                    
 
131
                    if (current == null) continue;                   
 
132
                }
 
133
                else {
 
134
                    keys = split(currentData);   
 
135
                }
 
136
                
 
137
                for (int j = 0; j < keys.length; j++) {
 
138
                    current = findNthChildElement(current, keys[j]);
 
139
                    if (current == null) break;
 
140
                }
 
141
            
 
142
                if (current != doc && current != null) {
 
143
                    if (!found) result.append(current); 
 
144
                    found = true;
 
145
                }
 
146
              
 
147
            }
 
148
            
 
149
        }
 
150
        
 
151
        if (found) return result;
 
152
        else {
 
153
            // If we get here and still haven't been able to match an
 
154
            // element, the XPointer has failed. 
 
155
            throw new XPointerResourceException(
 
156
              "XPointer " + xptr 
 
157
              + " did not locate any nodes in the document "
 
158
              + doc.getBaseURI()
 
159
            );
 
160
        }
 
161
        
 
162
    }
 
163
    
 
164
    
 
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;   
 
173
                elementCount++;
 
174
            }
 
175
        }
 
176
        return null;
 
177
    }
 
178
    
 
179
    
 
180
    private static int[] split(String tumbler)
 
181
      throws XPointerSyntaxException {
 
182
  
 
183
        int numberOfParts = 0;
 
184
        for (int i = 0; i < tumbler.length(); i++) {
 
185
          if (tumbler.charAt(i) == '/') numberOfParts++;   
 
186
        }
 
187
        
 
188
        int[] result = new int[numberOfParts];
 
189
        int index = 0;
 
190
        StringBuffer part = new StringBuffer(3);
 
191
        try {
 
192
            for (int i = 1; i < tumbler.length(); i++) {
 
193
                if (tumbler.charAt(i) == '/') {
 
194
                    result[index] = Integer.parseInt(part.toString()); 
 
195
                    index++;
 
196
                    part = new StringBuffer(3);
 
197
                }   
 
198
                else {
 
199
                    part.append(tumbler.charAt(i));   
 
200
                }   
 
201
            }
 
202
            result[result.length-1] = Integer.parseInt(part.toString());
 
203
        }
 
204
        catch (NumberFormatException ex) {
 
205
            XPointerSyntaxException ex2 
 
206
              = new XPointerSyntaxException(tumbler
 
207
                + " is not syntactically correct", ex); 
 
208
            throw ex2; 
 
209
        }
 
210
        
 
211
        return result;
 
212
    }
 
213
    
 
214
    private static List findElementSchemeData(String xpointer) 
 
215
      throws XPointerSyntaxException {
 
216
        
 
217
        List result = new ArrayList(1);
 
218
        
 
219
        StringBuffer xptr = new StringBuffer(xpointer.trim());
 
220
        StringBuffer scheme = new StringBuffer();
 
221
        int i = 0;
 
222
        while (i < xptr.length()) {
 
223
            char c = xptr.charAt(i);
 
224
            if (c == '(') break;
 
225
            else scheme.append(c); 
 
226
            i++; 
 
227
        }
 
228
        
 
229
        // need to verify that scheme is a QName
 
230
        try {
 
231
            // ugly hack because Verifier isn't public
 
232
            new Element(scheme.toString(), "http://www.example.com/");   
 
233
        }
 
234
        catch (IllegalNameException ex) {
 
235
            throw new XPointerSyntaxException(ex.getMessage());   
 
236
        }
 
237
        
 
238
        int open = 1; // parentheses count
 
239
        i++;
 
240
        StringBuffer schemeData = new StringBuffer();
 
241
        try {
 
242
            while (open > 0) {
 
243
                char c = xptr.charAt(i);   
 
244
                if (c == '^') {
 
245
                    c = xptr.charAt(i+1);
 
246
                    schemeData.append(c);
 
247
                    if (c != '^' && c != '(' && c != ')') {
 
248
                        throw new XPointerSyntaxException(
 
249
                          "Illegal XPointer escape sequence"
 
250
                        );   
 
251
                    }
 
252
                    i++;
 
253
                }
 
254
                else if (c == '(') {
 
255
                    schemeData.append(c);
 
256
                    open++;   
 
257
                }
 
258
                else if (c == ')') {
 
259
                    open--;
 
260
                    if (open > 0) schemeData.append(c);
 
261
                }
 
262
                else {
 
263
                    schemeData.append(c);   
 
264
                }
 
265
                i++;
 
266
            }
 
267
        }
 
268
        catch (StringIndexOutOfBoundsException ex) {
 
269
            throw new XPointerSyntaxException("Unbalanced parentheses");   
 
270
        }
 
271
        
 
272
        if (scheme.toString().equals("element")) {
 
273
            result.add(schemeData.toString());
 
274
        }
 
275
 
 
276
        if (i + 1 < xptr.length()) {
 
277
            result.addAll(findElementSchemeData(xptr.substring(i)));
 
278
        } 
 
279
        
 
280
        return result;
 
281
    }
 
282
    
 
283
    
 
284
    static Element findByID(Element element, String id) {
 
285
         
 
286
        Node current = element;
 
287
        boolean end = false;
 
288
        int index = -1;
 
289
        while (true) {
 
290
            
 
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;   
 
298
                        }
 
299
                    }   
 
300
                }
 
301
            }
 
302
            
 
303
            if (!end && current.getChildCount() > 0) {
 
304
               current = current.getChild(0);
 
305
               index = 0;
 
306
            }
 
307
            else {
 
308
                if (end) {
 
309
                    if (current == element) break;
 
310
                }
 
311
                end = false;
 
312
                ParentNode parent = current.getParent();
 
313
                if (parent.getChildCount() - 1 == index) {
 
314
                    current = parent;
 
315
                    if (current != element) {
 
316
                        parent = current.getParent();
 
317
                        index = parent.indexOf(current);
 
318
                    }
 
319
                    end = true;
 
320
                }
 
321
                else {
 
322
                    index++;
 
323
                    current = parent.getChild(index);
 
324
                }
 
325
            }
 
326
        }  
 
327
        
 
328
        return null;
 
329
        
 
330
    }
 
331
 
 
332
    
 
333
}