~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to core/src/org/netbeans/core/filesystems/XMLMIMEComponent.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
6
 * The contents of this file are subject to the terms of either the GNU
 
7
 * General Public License Version 2 only ("GPL") or the Common
 
8
 * Development and Distribution License("CDDL") (collectively, the
 
9
 * "License"). You may not use this file except in compliance with the
 
10
 * License. You can obtain a copy of the License at
 
11
 * http://www.netbeans.org/cddl-gplv2.html
 
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 
13
 * specific language governing permissions and limitations under the
 
14
 * License.  When distributing the software, include this License Header
 
15
 * Notice in each file and include the License file at
 
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 
17
 * particular file as subject to the "Classpath" exception as provided
 
18
 * by Sun in the GPL Version 2 section of the License file that
 
19
 * accompanied this code. If applicable, add the following below the
 
20
 * License Header, with the fields enclosed by brackets [] replaced by
 
21
 * your own identifying information:
 
22
 * "Portions Copyrighted [year] [name of copyright owner]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * The Original Software is NetBeans. The Initial Developer of the Original
 
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 
28
 * Microsystems, Inc. All Rights Reserved.
 
29
 *
 
30
 * If you wish your version of this file to be governed by only the CDDL
 
31
 * or only the GPL Version 2, indicate your decision by adding
 
32
 * "[Contributor] elects to include this software in this distribution
 
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 
34
 * single choice of license, a recipient has the option to distribute
 
35
 * your version of this file under either the CDDL, the GPL Version 2 or
 
36
 * to extend the choice of license to its licensees as provided above.
 
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 
38
 * Version 2 license, then the option applies only if the new code is
 
39
 * made subject to such option by the copyright holder.
 
40
 */
 
41
 
 
42
package org.netbeans.core.filesystems;
 
43
 
 
44
import java.lang.ref.WeakReference;
 
45
import java.util.logging.Level;
 
46
import java.util.logging.Logger;
 
47
import org.openide.filesystems.FileObject;
 
48
import org.openide.util.NbBundle;
 
49
import org.openide.xml.XMLUtil;
 
50
import org.xml.sax.Attributes;
 
51
import org.xml.sax.SAXException;
 
52
import org.xml.sax.SAXParseException;
 
53
import org.xml.sax.XMLReader;
 
54
import org.xml.sax.ext.LexicalHandler;
 
55
 
 
56
/**
 
57
 * This source represents a <b>XML rules</b> core plugin to <tt>MIMEReolverImpl</tt>.
 
58
 *
 
59
 * @author  Petr Kuzel
 
60
 * @version
 
61
 */
 
62
final class XMLMIMEComponent extends DefaultParser implements MIMEComponent {
 
63
 
 
64
    private short parseState = INIT;
 
65
    
 
66
    // template obtained form parsed description
 
67
    private final Smell template = new Smell();
 
68
 
 
69
    // cached and reused parser used for sniffing    
 
70
    private static final LocalSniffingParser local = new LocalSniffingParser();
 
71
 
 
72
    // FileObjectFilter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
73
 
 
74
    public boolean acceptFileObject(FileObject fo) {
 
75
 
 
76
        // it may come from arbitrary thread
 
77
        // retrive per thread instance
 
78
        
 
79
        SniffingParser sniffer = local.getParser();
 
80
        Smell print = sniffer.sniff(fo);
 
81
//        System.err.println("Print of " + fo);
 
82
//        System.err.println("print " + print);
 
83
//        System.err.println("template " + template);
 
84
        return template.match(print);
 
85
    }
 
86
 
 
87
    public String toString() {
 
88
       return template.toString();
 
89
    }
 
90
 
 
91
    // XML description -> memory representation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
92
 
 
93
 
 
94
    // pseudo validation states
 
95
    private static final short INIT = 0;
 
96
    private static final short IN_ROOT = 1;
 
97
    private static final short IN_DOCTYPE = 2;
 
98
    private static final short IN_ELEMENT = 3;
 
99
 
 
100
    // grammar elements
 
101
    private static final String ROOT = "xml-rule"; // NOI18N
 
102
    private static final String PI = "pi"; // NOI18N
 
103
    private static final String ELEMENT = "element"; // NOI18N
 
104
    private static final String DOCTYPE  = "doctype"; // NOI18N
 
105
    private static final String PUBLIC_ID = "public-id"; // NOI18N
 
106
    private static final String ID = "id"; // NOI18N
 
107
    private static final String ATTR = "attr"; // NOI18N
 
108
    private static final String NAME = "name"; // NOI18N
 
109
    private static final String VALUE = "text"; // NOI18N
 
110
    private static final String NS = "ns"; // NOI18N
 
111
    private static final String TARGET = "target"; // NOI18N
 
112
 
 
113
 
 
114
    public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
 
115
 
 
116
        String s;
 
117
 
 
118
        switch (parseState) {
 
119
 
 
120
            case INIT:
 
121
                if (ROOT.equals(qName) ==  false) error();
 
122
                parseState = IN_ROOT;
 
123
                break;
 
124
 
 
125
            case IN_ROOT:
 
126
                if (PI.equals(qName)) {
 
127
                    s = atts.getValue(TARGET); if (s == null) error();
 
128
                    template.addPI(s);
 
129
 
 
130
                    //!!! TODO presudo atts
 
131
 
 
132
                } else if (DOCTYPE.equals(qName)) {
 
133
                    s = atts.getValue(PUBLIC_ID);
 
134
                    if (s == null) {
 
135
                        parseState = IN_DOCTYPE;
 
136
                        break;
 
137
                    } else {
 
138
                        template.addDoctype(s);
 
139
                    }
 
140
 
 
141
                } else if (ELEMENT.equals(qName)) {
 
142
 
 
143
                    s = atts.getValue(NAME);
 
144
                    if (s == null) {
 
145
                        s = atts.getValue(NS);
 
146
                        if (s != null) template.addElementNS(s);
 
147
                    } else {
 
148
                        template.addElementName(s);
 
149
                        s = atts.getValue(NS);
 
150
                        if (s != null) template.addElementNS(s);
 
151
                    }
 
152
 
 
153
                    parseState = IN_ELEMENT;
 
154
 
 
155
                } else {
 
156
                    error();
 
157
                }
 
158
                break;
 
159
 
 
160
            case IN_DOCTYPE:
 
161
                if (PUBLIC_ID.equals(qName) == false) error();
 
162
                s = atts.getValue(ID); if (s == null) error();
 
163
                template.addDoctype(s);
 
164
                break;
 
165
 
 
166
            case IN_ELEMENT:
 
167
                if (ATTR.equals(qName)) {
 
168
                    s = atts.getValue(NAME); if (s == null) error();
 
169
                    template.addElementAtt(s, atts.getValue(VALUE));
 
170
 
 
171
                } else if (NS.equals(qName)) {
 
172
                    s = atts.getValue(NAME); if (s == null) error();
 
173
                    template.addElementNS(s);
 
174
 
 
175
                } else {
 
176
                    error();
 
177
                }
 
178
 
 
179
        }
 
180
    }
 
181
 
 
182
    public void endElement(String namespaceURI, String localName, String qName) {
 
183
 
 
184
        switch (parseState) {
 
185
            case IN_ELEMENT:
 
186
                if (ELEMENT.equals(qName)) parseState = IN_ROOT;
 
187
                break;      
 
188
 
 
189
            case IN_DOCTYPE:
 
190
                if (DOCTYPE.equals(qName)) parseState = IN_ROOT;
 
191
                break;
 
192
        }
 
193
    }
 
194
    
 
195
    // Sniffing parser ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
196
    
 
197
 
 
198
    /**
 
199
     * Create just one shared parser instance per thread.
 
200
     * Consequently one instance cannot be run in paralel eliminating need for sync.
 
201
     */
 
202
    private static class LocalSniffingParser extends ThreadLocal<WeakReference<SniffingParser>> {
 
203
        LocalSniffingParser() {}
 
204
        
 
205
        private WeakReference<SniffingParser> wref = null;
 
206
        
 
207
        protected WeakReference<SniffingParser> initialValue() {            
 
208
            SniffingParser parser = new SniffingParser();
 
209
            wref = new WeakReference<SniffingParser>(parser);
 
210
            return wref;
 
211
        }
 
212
        
 
213
        public SniffingParser getParser() {
 
214
            WeakReference<SniffingParser> cache = get();
 
215
            SniffingParser cached = cache.get();
 
216
            if (cached == null) {
 
217
                cached = new SniffingParser();
 
218
                wref = new WeakReference<SniffingParser>(cached);                
 
219
                super.set(wref);
 
220
            }
 
221
            return cached;            
 
222
        }
 
223
        
 
224
        public void set(WeakReference<SniffingParser> data) {
 
225
            // we are read only!
 
226
        }
 
227
    }
 
228
 
 
229
        
 
230
    /**
 
231
     * Parser that test XML Document header.
 
232
     */
 
233
    private static class SniffingParser extends DefaultParser implements LexicalHandler {
 
234
 
 
235
        SniffingParser() {
 
236
            super(null);
 
237
        }
 
238
 
 
239
        // last succesfully sniffed fileobject
 
240
        private FileObject lastFileObject = null;
 
241
        
 
242
        private Smell print = null;
 
243
        
 
244
        // the only way how to stop parser is throwing an exception
 
245
        private static final SAXException STOP = new SAXException("STOP");  //NOI18N
 
246
 
 
247
        /**
 
248
         * Go ahead and retrieve a print or null
 
249
         */
 
250
        protected Smell sniff(FileObject fo) {
 
251
 
 
252
            if (fo == null) return null;
 
253
            
 
254
            if (fo.equals(lastFileObject)) return print;
 
255
            
 
256
            if (fo.isValid() == false) return null;
 
257
 
 
258
            if (fo.getSize() == 0) return null;
 
259
            
 
260
            print = new Smell();
 
261
            parse(fo);
 
262
            if (this.state == ERROR) {
 
263
                return null;
 
264
            }
 
265
            
 
266
            lastFileObject = fo;
 
267
            return print;
 
268
        }
 
269
        
 
270
        protected XMLReader createXMLReader() {
 
271
            XMLReader parser = null;
 
272
            
 
273
            try {
 
274
                parser = XMLUtil.createXMLReader(false, true);           
 
275
                try {
 
276
                    parser.setProperty("http://xml.org/sax/properties/lexical-handler", this);  //NOI18N
 
277
                } catch (SAXException sex) {
 
278
                    Logger.getLogger(XMLMIMEComponent.class.getName()).fine(NbBundle.getMessage(XMLMIMEComponent.class, "W-003"));  //NOI18N
 
279
                }
 
280
            } catch (SAXException ex) {
 
281
                Logger.getLogger(XMLMIMEComponent.class.getName()).log(Level.WARNING, null, ex);
 
282
            }
 
283
            return parser;
 
284
        }
 
285
        
 
286
        protected boolean isStopException(Exception e) {
 
287
            return STOP.getMessage().equals(e.getMessage());
 
288
        }        
 
289
        
 
290
        
 
291
        public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {            
 
292
            if (namespaceURI != null) {
 
293
                print.addElementNS(namespaceURI);
 
294
            }
 
295
            if ("".equals(localName)) localName = null;  //#16484  //NOI18N
 
296
            print.addElementName(localName != null ? localName : qName);
 
297
            for (int i = 0; i<atts.getLength(); i++) {
 
298
                print.addElementAtt(atts.getQName(i), atts.getValue(i));
 
299
            }
 
300
            throw STOP;
 
301
        }
 
302
        
 
303
        public void processingInstruction(String target, String data) throws SAXException {
 
304
            print.addPI(target);
 
305
        }
 
306
        
 
307
        // LexicalHandler
 
308
 
 
309
        public void startDTD(String root, String pID, String sID) throws SAXException {
 
310
            print.addDoctype(pID);
 
311
        }
 
312
 
 
313
        public void endDTD() {}
 
314
 
 
315
        public void startEntity(String name) {}
 
316
 
 
317
        public void endEntity(String name) {}
 
318
 
 
319
        public void startCDATA() {}
 
320
 
 
321
        public void endCDATA() {}
 
322
 
 
323
        public void comment(char[] ch, int start, int length) {}
 
324
        
 
325
        public void error(SAXParseException exception) throws SAXException {            
 
326
            // we are not validating should not occure
 
327
            Logger.getLogger(XMLMIMEComponent.class.getName()).warning(exception.getMessage());
 
328
            this.state = ERROR;
 
329
            throw STOP;
 
330
        }
 
331
 
 
332
        public void fatalError(SAXParseException exception) throws SAXException {
 
333
 
 
334
            // it may be caused by wrong user XML documents, notify only in debug mode
 
335
            // also see #16484 if the error message makes no sense
 
336
            Logger emgr = Logger.getLogger("org.netbeans.core.filesystems.XMLMIMEComponent"); // NOI18N
 
337
            if (emgr.isLoggable(Level.FINE)) {
 
338
                emgr.fine("[while parsing " + fo + "] " + exception.getSystemId() + ":" + exception.getLineNumber() + ":" + exception.getColumnNumber() + ": " + exception.getMessage()); // NOI18N
 
339
            }
 
340
 
 
341
            this.state = ERROR;
 
342
            throw STOP;
 
343
        }
 
344
        
 
345
        
 
346
    }
 
347
 
 
348
    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
349
    
 
350
    /** 
 
351
     * Template smell per resolver and print data per FileObject.
 
352
     */
 
353
    private static class Smell {
 
354
        Smell() {}
 
355
        
 
356
        private String[] doctypes = null;
 
357
        private String[] pis = null;
 
358
        
 
359
        private String   root = null;
 
360
        private String[] nss = null;
 
361
        
 
362
        private String[] attns = null;
 
363
        private String[] attvs = null;
 
364
 
 
365
        public String toString() {
 
366
            StringBuffer buf = new StringBuffer();
 
367
            int i = 0;
 
368
            buf.append("xml-check(");
 
369
            
 
370
            if (doctypes != null) {
 
371
                buf.append("doctypes:");
 
372
                for (i = 0; i<doctypes.length; i++)
 
373
                    buf.append(doctypes[i]).append(", ");
 
374
            }
 
375
 
 
376
            if (pis != null) {
 
377
                buf.append("PIs:");
 
378
                for (i = 0; i<pis.length; i++)
 
379
                    buf.append(pis[i]).append(", ");
 
380
            }
 
381
 
 
382
            if (root != null) {
 
383
               buf.append("root:").append(root);
 
384
            }
 
385
 
 
386
            if (nss != null) {
 
387
                buf.append("root-namespaces:");
 
388
                for (i = 0; i<nss.length; i++)
 
389
                    buf.append(nss[i]).append(", ");
 
390
            }
 
391
 
 
392
            if (attns != null) {
 
393
                buf.append("attributes:");
 
394
                for (i = 0; i<attns.length; i++)
 
395
                    buf.append(attns[i]).append("='").append(attvs[i]).append("'");
 
396
            }
 
397
 
 
398
            buf.append(')');
 
399
            return buf.toString();
 
400
 
 
401
        }
 
402
 
 
403
        private void addDoctype(String s) {
 
404
            if (doctypes == null) {
 
405
                doctypes = new String[] { s };
 
406
            } else {
 
407
                doctypes = Util.addString(doctypes, s);
 
408
            }            
 
409
        }
 
410
        
 
411
        private void addPI(String s) {
 
412
            if (pis == null) {
 
413
                pis = new String[] { s };
 
414
            } else {
 
415
                pis = Util.addString(pis, s);
 
416
            }                        
 
417
        }
 
418
        
 
419
        private void addElementNS(String s) {
 
420
            if (nss == null) {
 
421
                nss = new String[] { s };
 
422
            } else {
 
423
                nss = Util.addString(nss, s);
 
424
            }                        
 
425
        }
 
426
        
 
427
        private void addElementName(String name) {
 
428
            root = name;
 
429
        }
 
430
        
 
431
        private void addElementAtt(String name, String value) {
 
432
            if (attns == null) {
 
433
                attns = new String[] {name};
 
434
                attvs = new String[] {value};
 
435
            } else {
 
436
                attns = Util.addString(attns, name);
 
437
                attvs = Util.addString(attvs, value);
 
438
            }
 
439
            
 
440
        }
 
441
 
 
442
        /**
 
443
         * Matches passed data this template?
 
444
         * Any of constructs must match.
 
445
         */
 
446
        public boolean match(Smell t) {
 
447
 
 
448
            if (t == null) return false;
 
449
            
 
450
            // try if a doctype public-id matches
 
451
            
 
452
            if (doctypes != null && t.doctypes != null) {
 
453
                if (Util.contains(doctypes, t.doctypes[0])) return true;
 
454
            }
 
455
            
 
456
            // try root element match
 
457
            
 
458
            if (root != null && root.equals(t.root)) {
 
459
                if (nss == null) {                                                            
 
460
                    if (attMatch(t)) return true;
 
461
                } else {                                        
 
462
                    if (t.nss != null && Util.contains(nss, t.nss[0])) {
 
463
                        if (attMatch(t)) return true;
 
464
                    }                    
 
465
                }
 
466
            } else {
 
467
                if (root == null && nss != null && t.nss != null && Util.contains(nss, t.nss[0])) {
 
468
                    if (attMatch(t)) return true;
 
469
                }                                    
 
470
            }
 
471
            
 
472
            // try if a PI matches
 
473
            
 
474
            if (pis != null && t.pis!=null) {
 
475
                for (int i = 0; i<pis.length; i++) {
 
476
                    for (int j = 0; j<t.pis.length; j++) {
 
477
                        if (pis[i].equals(t.pis[j])) return true;
 
478
                    }
 
479
                }
 
480
            }            
 
481
            
 
482
            return false;
 
483
        }
 
484
        
 
485
        
 
486
        private boolean attMatch(Smell t) {
 
487
 
 
488
            if (attns == null) return true;
 
489
            if (t.attns == null) return false;
 
490
            
 
491
            // all attributes must match by name ...
 
492
            for (int i = 0 ; i<attns.length; i++) {
 
493
                int match = Util.indexOf(t.attns, attns[i]);
 
494
                if (match == -1) {
 
495
                    return false;
 
496
                }
 
497
 
 
498
                // ... and value if specified in template
 
499
 
 
500
                if (attvs[i] != null && (!attvs[i].equals(t.attvs[match]))) {
 
501
                    return  false;
 
502
                }
 
503
            }
 
504
            
 
505
            return true;
 
506
            
 
507
        }
 
508
 
 
509
    }
 
510
}