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

« back to all changes in this revision

Viewing changes to java/source/src/org/netbeans/modules/java/source/parsing/SourceFileObject.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.modules.java.source.parsing;
 
43
 
 
44
import java.io.BufferedInputStream;
 
45
import java.io.ByteArrayInputStream;
 
46
import java.io.IOException;
 
47
import java.io.InputStream;
 
48
import java.io.InputStreamReader;
 
49
import java.io.OutputStream;
 
50
import java.io.OutputStreamWriter;
 
51
import java.io.Reader;
 
52
import java.io.StringReader;
 
53
import java.io.UnsupportedEncodingException;
 
54
import java.net.URI;
 
55
import java.nio.CharBuffer;
 
56
import java.util.Set;
 
57
import javax.lang.model.element.Modifier;
 
58
import javax.lang.model.element.NestingKind;
 
59
import javax.swing.text.BadLocationException;
 
60
import javax.swing.text.Document;
 
61
import javax.swing.text.StyledDocument;
 
62
import javax.tools.JavaFileObject;
 
63
import org.netbeans.api.java.lexer.JavaTokenId;
 
64
import org.netbeans.api.lexer.TokenHierarchy;
 
65
import org.netbeans.api.queries.FileEncodingQuery;
 
66
import org.netbeans.modules.java.preprocessorbridge.spi.JavaFileFilterImplementation;
 
67
import org.openide.ErrorManager;
 
68
import org.openide.cookies.EditorCookie;
 
69
import org.openide.filesystems.FileLock;
 
70
import org.openide.filesystems.FileObject;
 
71
import org.openide.filesystems.FileStateInvalidException;
 
72
import org.openide.filesystems.JarFileSystem;
 
73
import org.openide.loaders.DataObject;
 
74
import org.openide.loaders.DataObjectNotFoundException;
 
75
import org.openide.text.NbDocument;
 
76
 
 
77
/**
 
78
 *
 
79
 * @author Tomas Zezula
 
80
 */
 
81
public class SourceFileObject implements JavaFileObject, DocumentProvider {    
 
82
    
 
83
    final FileObject file;
 
84
    final FileObject root;
 
85
    private final Kind kind;
 
86
    private URI uri;        //Cache for URI
 
87
    private String text;
 
88
    private TokenHierarchy<?> tokens;
 
89
    private final JavaFileFilterImplementation filter;
 
90
    
 
91
    public static SourceFileObject create (final FileObject file, final FileObject root) {        
 
92
        try {
 
93
            return new SourceFileObject (file, root, null, false);
 
94
        } catch (IOException ioe) {
 
95
            ErrorManager.getDefault().notify(ioe);
 
96
            return null;
 
97
        }        
 
98
    }
 
99
    
 
100
    /** Creates a new instance of SourceFileObject */
 
101
    public SourceFileObject (final FileObject file, final FileObject root, final JavaFileFilterImplementation filter, final boolean renderNow) throws IOException {
 
102
        assert file != null;
 
103
        this.file = file;
 
104
        this.root = root;
 
105
        this.filter = filter;
 
106
        String ext = this.file.getExt();        
 
107
        this.kind = FileObjects.getKind(ext);        
 
108
        if (renderNow && this.kind != Kind.CLASS) {
 
109
            getCharContentImpl(true);
 
110
        }
 
111
    }
 
112
 
 
113
    
 
114
 
 
115
    public boolean isNameCompatible (String simplename, JavaFileObject.Kind kind) {
 
116
        assert simplename != null;
 
117
        return this.kind == kind && this.getNameWithoutExtension().equals(simplename);
 
118
    }
 
119
 
 
120
    public CharBuffer getCharContent(boolean ignoreEncodingErrors) throws IOException {
 
121
        String _text;
 
122
        synchronized (this) {
 
123
            _text = this.text;
 
124
        }
 
125
        if (_text != null) {
 
126
            return CharBuffer.wrap(_text);
 
127
        }
 
128
        else {
 
129
            return getCharContentImpl(false);
 
130
        }
 
131
    }
 
132
    
 
133
    public TokenHierarchy<?> getTokenHierarchy() throws IOException {
 
134
        if (tokens == null)
 
135
            getCharContentImpl(false);
 
136
        
 
137
        return tokens;
 
138
    }
 
139
   
 
140
 
 
141
    public java.io.Writer openWriter() throws IOException {
 
142
        return new OutputStreamWriter (this.openOutputStream(), FileEncodingQuery.getEncoding(file));
 
143
    }
 
144
 
 
145
    public Reader openReader(boolean ignoreEncodingErrors) throws IOException {        
 
146
        String _text;
 
147
        synchronized (this) {
 
148
            _text = text;
 
149
        }
 
150
        if (_text != null) {
 
151
            return new StringReader (_text);
 
152
        }
 
153
        else {
 
154
            final Document doc = getDocument(isOpened());
 
155
            if (doc == null) {
 
156
                Reader r = new InputStreamReader (new BufferedInputStream (this.file.getInputStream()),FileEncodingQuery.getEncoding(file));
 
157
                if (filter != null) {
 
158
                    r = filter.filterReader(r);
 
159
                }
 
160
                return r;
 
161
            }
 
162
            else {
 
163
                final StringBuilder builder = new StringBuilder ();
 
164
                doc.render(new Runnable() {
 
165
                    public void run () {
 
166
                      try {
 
167
                            builder.append(doc.getText(0, doc.getLength()));
 
168
                        } catch (BadLocationException e) {
 
169
                            ErrorManager.getDefault().notify(e);
 
170
                        }  
 
171
                    }
 
172
                });
 
173
                return new StringReader (builder.toString());
 
174
            }
 
175
        }       
 
176
    }
 
177
 
 
178
    public java.io.OutputStream openOutputStream() throws IOException {
 
179
        final StyledDocument doc = getDocument(isOpened());
 
180
        if (doc == null) {
 
181
            return new LckStream (this.file);
 
182
        }
 
183
        else {
 
184
            return new DocumentStream (doc);
 
185
        }
 
186
    }
 
187
 
 
188
    public InputStream openInputStream() throws IOException {
 
189
        String _text;
 
190
        synchronized (this) {
 
191
            _text = text;
 
192
        }
 
193
        if (_text != null) {
 
194
            return new ByteArrayInputStream (_text.getBytes());
 
195
        }
 
196
        else {
 
197
            final Document doc = getDocument(isOpened());
 
198
            if (doc == null) {
 
199
                return this.file.getInputStream();
 
200
            }
 
201
            else {
 
202
                final StringBuilder builder = new StringBuilder ();
 
203
                doc.render(new Runnable() {
 
204
                    public void run () {
 
205
                      try {
 
206
                            builder.append(doc.getText(0, doc.getLength()));
 
207
                        } catch (BadLocationException e) {
 
208
                            ErrorManager.getDefault().notify(e);
 
209
                        }  
 
210
                    }
 
211
                });
 
212
                return new ByteArrayInputStream (builder.toString().getBytes());
 
213
            }
 
214
        }
 
215
    }
 
216
 
 
217
    public boolean delete() {
 
218
        if (isModified()!=null) {
 
219
            //If the file is modified in editor do not delete it
 
220
            return false;
 
221
        }
 
222
        else {
 
223
            try {
 
224
                FileLock lock = this.file.lock();
 
225
                try {
 
226
                    this.file.delete (lock);
 
227
                    return true;
 
228
                }
 
229
                finally {
 
230
                    lock.releaseLock();
 
231
                }
 
232
            } catch (IOException e) {
 
233
                return false;
 
234
            }
 
235
        }
 
236
    }
 
237
 
 
238
 
 
239
    public JavaFileObject.Kind getKind() {
 
240
        return this.kind;
 
241
    }
 
242
 
 
243
    public String getName() {
 
244
       return this.file.getNameExt();
 
245
    }
 
246
 
 
247
    public String getNameWithoutExtension() {
 
248
        return this.file.getName();
 
249
    }
 
250
    
 
251
    public synchronized URI toUri () {
 
252
        if (this.uri == null) {
 
253
            try {            
 
254
                this.uri = URI.create(this.file.getURL().toExternalForm());
 
255
            } catch (FileStateInvalidException e) {
 
256
                ErrorManager.getDefault().notify(e);                
 
257
            }
 
258
        }
 
259
        return this.uri;
 
260
    }
 
261
 
 
262
    /**
 
263
     * Returns the mtime of the file, in the case of opened
 
264
     * modified file, the mtime is not known, this method returns
 
265
     * the current time.
 
266
     */
 
267
    public long getLastModified() {
 
268
        if (isModified()==null) {
 
269
            try {
 
270
                //Prefer class files to packed sources, the packed sources may have wrong time stamps.
 
271
                if (this.file.getFileSystem() instanceof JarFileSystem) {
 
272
                    return 0L;
 
273
                }
 
274
            } catch (FileStateInvalidException e) {
 
275
                //Handled below
 
276
            }
 
277
            return this.file.lastModified().getTime();
 
278
        }
 
279
        else {
 
280
            return System.currentTimeMillis();
 
281
        }
 
282
    }
 
283
    
 
284
    public NestingKind getNestingKind() {
 
285
        return null;
 
286
    }
 
287
 
 
288
    public Modifier getAccessLevel() {
 
289
        return null;
 
290
    }
 
291
    
 
292
    public @Override String toString () {
 
293
        return this.file.getPath();
 
294
    }
 
295
    
 
296
    public @Override boolean equals (Object other) {
 
297
        if (other instanceof SourceFileObject) {
 
298
            SourceFileObject otherSource = (SourceFileObject) other;
 
299
            return this.file.equals (otherSource.file);
 
300
        }
 
301
        else {
 
302
            return false;
 
303
        }
 
304
    }
 
305
    
 
306
    public @Override int hashCode () {
 
307
        return this.file.hashCode();
 
308
    }
 
309
    
 
310
    public StyledDocument getDocument() {
 
311
        EditorCookie ec = isOpened();
 
312
        return ec != null ? getDocument(ec) : null;       
 
313
    }
 
314
    
 
315
    public void runAtomic(final Runnable r) {
 
316
        assert r != null;
 
317
        final StyledDocument doc = getDocument();
 
318
        if (doc == null) {
 
319
            throw new IllegalStateException ();
 
320
        }
 
321
        else {
 
322
            NbDocument.runAtomic(doc,r);
 
323
        }
 
324
    }
 
325
    
 
326
    @SuppressWarnings ("unchecked")     // NOI18N
 
327
    private EditorCookie isModified () {
 
328
        DataObject.Registry regs = DataObject.getRegistry();
 
329
        Set<DataObject> modified = regs.getModifiedSet();
 
330
        for (DataObject dobj : modified) {
 
331
            if (this.file.equals(dobj.getPrimaryFile())) {
 
332
                EditorCookie ec = dobj.getCookie(EditorCookie.class);
 
333
                return ec;
 
334
            }
 
335
        }
 
336
        return null;
 
337
    }
 
338
    
 
339
    public EditorCookie isOpened () {
 
340
        try {
 
341
            if (this.kind == JavaFileObject.Kind.CLASS) {
 
342
                return null;
 
343
            }
 
344
            DataObject dobj = DataObject.find(this.file);
 
345
            return dobj.getCookie(EditorCookie.class);
 
346
        } catch (DataObjectNotFoundException dnf) {
 
347
            return null;
 
348
        }
 
349
    }
 
350
    
 
351
    private CharBuffer getCharContentImpl (boolean assign) throws IOException {
 
352
        final Document doc = getDocument(isOpened());
 
353
        char[] result = null;
 
354
        int length = 0;
 
355
        if (doc == null) {
 
356
            Reader in = this.openReader (true);
 
357
            int red = 0, rv;
 
358
            try {
 
359
                int len = (int)this.file.getSize();
 
360
                result = new char [len+1];
 
361
                while ((rv=in.read(result,red,len-red))>0 && (red=red+rv)<len);
 
362
            } finally {
 
363
                in.close();
 
364
            }
 
365
            int j=0;
 
366
            for (int i=0; i<red;i++) {
 
367
                if (result[i] =='\r') {                                          //NOI18N
 
368
                    if (i+1>=red || result[i+1]!='\n') {                         //NOI18N
 
369
                        result[j++] = '\n';                                      //NOI18N
 
370
                    }
 
371
                }
 
372
                else {
 
373
                    result[j++] = result[i];
 
374
                }
 
375
            }
 
376
            length = j;
 
377
        }
 
378
        else {            
 
379
            final CharSequence[] _text = new CharSequence[1];
 
380
            doc.render(new Runnable() {
 
381
                public void run () {
 
382
                    try {
 
383
                        _text[0] = doc.getText(0, doc.getLength());
 
384
                    } catch (BadLocationException e) {
 
385
                        ErrorManager.getDefault().notify(e);
 
386
                    }
 
387
                }
 
388
            });
 
389
            if (_text[0] != null) {
 
390
                if (filter != null) {
 
391
                    _text[0] = filter.filterCharSequence(_text[0]);
 
392
                }
 
393
                int len = _text[0].length();
 
394
                result = new char[len+1];
 
395
                _text[0].toString().getChars(0,len,result,0);
 
396
                length = len;
 
397
            }
 
398
        }
 
399
        result[length]='\n'; //NOI18N
 
400
        
 
401
        String str = new String(result,0,length);
 
402
        CharBuffer charBuffer = CharBuffer.wrap (str);
 
403
        tokens = TokenHierarchy.create(charBuffer, false, JavaTokenId.language(), null, null); //TODO: .createSnapshot();
 
404
        if (assign) text = str;
 
405
        return charBuffer;
 
406
    }
 
407
            
 
408
    private static StyledDocument getDocument (EditorCookie ec) {
 
409
        return ec == null ? null : ec.getDocument();
 
410
    }    
 
411
    
 
412
    
 
413
    private class LckStream extends OutputStream {
 
414
        
 
415
        private final OutputStream delegate;
 
416
        private final FileLock lock;
 
417
        
 
418
        public LckStream (final FileObject fo) throws IOException {
 
419
            assert fo != null;
 
420
            this.lock = fo.lock();
 
421
            try {
 
422
                this.delegate = fo.getOutputStream (this.lock);
 
423
            } finally {
 
424
                if (this.delegate == null) {
 
425
                    this.lock.releaseLock();
 
426
                }
 
427
            }
 
428
        }
 
429
 
 
430
        public @Override void write(byte[] b, int off, int len) throws IOException {
 
431
            this.delegate.write(b, off, len);
 
432
        }
 
433
 
 
434
        public @Override void write(byte[] b) throws IOException {
 
435
            this.delegate.write(b);
 
436
        }
 
437
 
 
438
        public void write(int b) throws IOException {
 
439
            this.delegate.write (b);
 
440
        }
 
441
 
 
442
        public @Override void close() throws IOException {
 
443
            try {
 
444
                this.delegate.close();
 
445
            } finally {
 
446
                this.lock.releaseLock();
 
447
                synchronized (SourceFileObject.this) {
 
448
                    text = null;
 
449
                }
 
450
            }            
 
451
        }                
 
452
    }
 
453
    
 
454
    private class DocumentStream extends OutputStream {
 
455
        
 
456
        private static final int BUF_SIZ=2048;
 
457
        
 
458
        private final StyledDocument doc;
 
459
        private byte[] data;
 
460
        private int pos;
 
461
            
 
462
        public DocumentStream (final StyledDocument doc) {
 
463
            assert doc != null;
 
464
            this.doc = doc;
 
465
            this.data = new byte[BUF_SIZ];
 
466
            this.pos = 0;
 
467
        }
 
468
        
 
469
        public synchronized @Override void write(byte[] b, int off, int len) throws IOException {
 
470
            ensureSize (len);
 
471
            System.arraycopy(b,off,this.data,this.pos,len);
 
472
            this.pos+=len;
 
473
        }
 
474
 
 
475
        public synchronized @Override void write(byte[] b) throws IOException {
 
476
            ensureSize (b.length);
 
477
            System.arraycopy(b,0,this.data,this.pos,b.length);
 
478
            this.pos+=b.length;
 
479
        }
 
480
 
 
481
        public synchronized void write(int b) throws IOException {
 
482
            ensureSize (1);
 
483
            this.data[this.pos++]=(byte)(b&0xff);
 
484
        }
 
485
        
 
486
        private void ensureSize (int delta) {
 
487
            int requiredLength = this.pos + delta;
 
488
            if (this.data.length<requiredLength) {
 
489
                int newSize = this.data.length + BUF_SIZ;
 
490
                while (newSize<requiredLength) {
 
491
                    newSize+=BUF_SIZ;
 
492
                }
 
493
                byte[] newData = new byte[newSize];
 
494
                System.arraycopy(this.data,0,newData,0,this.pos);
 
495
                this.data = newData;
 
496
            }
 
497
        }
 
498
        
 
499
        public synchronized @Override void close() throws IOException {
 
500
            try {
 
501
                NbDocument.runAtomic(this.doc,
 
502
                    new Runnable () {
 
503
                        public void run () {
 
504
                            try {
 
505
                                doc.remove(0,doc.getLength());
 
506
                                //todo: use new String(data,0,pos,FileEncodingQuery.getEncoding(file)) on JDK 6.0
 
507
                                doc.insertString(0,new String(data,0,pos,FileEncodingQuery.getEncoding(file).name()),null);
 
508
                            } catch (BadLocationException e) {
 
509
                                ErrorManager.getDefault().notify(e);
 
510
                            }
 
511
                            catch (UnsupportedEncodingException ee) {
 
512
                                ErrorManager.getDefault().notify (ee);
 
513
                        }
 
514
                        }
 
515
                    });
 
516
            } finally {
 
517
                synchronized (SourceFileObject.this) {
 
518
                    text = null;
 
519
                }
 
520
            }
 
521
        }        
 
522
    }
 
523
}