2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
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]"
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.
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.
42
package org.netbeans.modules.java.source.parsing;
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;
55
import java.nio.CharBuffer;
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;
79
* @author Tomas Zezula
81
public class SourceFileObject implements JavaFileObject, DocumentProvider {
83
final FileObject file;
84
final FileObject root;
85
private final Kind kind;
86
private URI uri; //Cache for URI
88
private TokenHierarchy<?> tokens;
89
private final JavaFileFilterImplementation filter;
91
public static SourceFileObject create (final FileObject file, final FileObject root) {
93
return new SourceFileObject (file, root, null, false);
94
} catch (IOException ioe) {
95
ErrorManager.getDefault().notify(ioe);
100
/** Creates a new instance of SourceFileObject */
101
public SourceFileObject (final FileObject file, final FileObject root, final JavaFileFilterImplementation filter, final boolean renderNow) throws IOException {
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);
115
public boolean isNameCompatible (String simplename, JavaFileObject.Kind kind) {
116
assert simplename != null;
117
return this.kind == kind && this.getNameWithoutExtension().equals(simplename);
120
public CharBuffer getCharContent(boolean ignoreEncodingErrors) throws IOException {
122
synchronized (this) {
126
return CharBuffer.wrap(_text);
129
return getCharContentImpl(false);
133
public TokenHierarchy<?> getTokenHierarchy() throws IOException {
135
getCharContentImpl(false);
141
public java.io.Writer openWriter() throws IOException {
142
return new OutputStreamWriter (this.openOutputStream(), FileEncodingQuery.getEncoding(file));
145
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
147
synchronized (this) {
151
return new StringReader (_text);
154
final Document doc = getDocument(isOpened());
156
Reader r = new InputStreamReader (new BufferedInputStream (this.file.getInputStream()),FileEncodingQuery.getEncoding(file));
157
if (filter != null) {
158
r = filter.filterReader(r);
163
final StringBuilder builder = new StringBuilder ();
164
doc.render(new Runnable() {
167
builder.append(doc.getText(0, doc.getLength()));
168
} catch (BadLocationException e) {
169
ErrorManager.getDefault().notify(e);
173
return new StringReader (builder.toString());
178
public java.io.OutputStream openOutputStream() throws IOException {
179
final StyledDocument doc = getDocument(isOpened());
181
return new LckStream (this.file);
184
return new DocumentStream (doc);
188
public InputStream openInputStream() throws IOException {
190
synchronized (this) {
194
return new ByteArrayInputStream (_text.getBytes());
197
final Document doc = getDocument(isOpened());
199
return this.file.getInputStream();
202
final StringBuilder builder = new StringBuilder ();
203
doc.render(new Runnable() {
206
builder.append(doc.getText(0, doc.getLength()));
207
} catch (BadLocationException e) {
208
ErrorManager.getDefault().notify(e);
212
return new ByteArrayInputStream (builder.toString().getBytes());
217
public boolean delete() {
218
if (isModified()!=null) {
219
//If the file is modified in editor do not delete it
224
FileLock lock = this.file.lock();
226
this.file.delete (lock);
232
} catch (IOException e) {
239
public JavaFileObject.Kind getKind() {
243
public String getName() {
244
return this.file.getNameExt();
247
public String getNameWithoutExtension() {
248
return this.file.getName();
251
public synchronized URI toUri () {
252
if (this.uri == null) {
254
this.uri = URI.create(this.file.getURL().toExternalForm());
255
} catch (FileStateInvalidException e) {
256
ErrorManager.getDefault().notify(e);
263
* Returns the mtime of the file, in the case of opened
264
* modified file, the mtime is not known, this method returns
267
public long getLastModified() {
268
if (isModified()==null) {
270
//Prefer class files to packed sources, the packed sources may have wrong time stamps.
271
if (this.file.getFileSystem() instanceof JarFileSystem) {
274
} catch (FileStateInvalidException e) {
277
return this.file.lastModified().getTime();
280
return System.currentTimeMillis();
284
public NestingKind getNestingKind() {
288
public Modifier getAccessLevel() {
292
public @Override String toString () {
293
return this.file.getPath();
296
public @Override boolean equals (Object other) {
297
if (other instanceof SourceFileObject) {
298
SourceFileObject otherSource = (SourceFileObject) other;
299
return this.file.equals (otherSource.file);
306
public @Override int hashCode () {
307
return this.file.hashCode();
310
public StyledDocument getDocument() {
311
EditorCookie ec = isOpened();
312
return ec != null ? getDocument(ec) : null;
315
public void runAtomic(final Runnable r) {
317
final StyledDocument doc = getDocument();
319
throw new IllegalStateException ();
322
NbDocument.runAtomic(doc,r);
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);
339
public EditorCookie isOpened () {
341
if (this.kind == JavaFileObject.Kind.CLASS) {
344
DataObject dobj = DataObject.find(this.file);
345
return dobj.getCookie(EditorCookie.class);
346
} catch (DataObjectNotFoundException dnf) {
351
private CharBuffer getCharContentImpl (boolean assign) throws IOException {
352
final Document doc = getDocument(isOpened());
353
char[] result = null;
356
Reader in = this.openReader (true);
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);
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
373
result[j++] = result[i];
379
final CharSequence[] _text = new CharSequence[1];
380
doc.render(new Runnable() {
383
_text[0] = doc.getText(0, doc.getLength());
384
} catch (BadLocationException e) {
385
ErrorManager.getDefault().notify(e);
389
if (_text[0] != null) {
390
if (filter != null) {
391
_text[0] = filter.filterCharSequence(_text[0]);
393
int len = _text[0].length();
394
result = new char[len+1];
395
_text[0].toString().getChars(0,len,result,0);
399
result[length]='\n'; //NOI18N
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;
408
private static StyledDocument getDocument (EditorCookie ec) {
409
return ec == null ? null : ec.getDocument();
413
private class LckStream extends OutputStream {
415
private final OutputStream delegate;
416
private final FileLock lock;
418
public LckStream (final FileObject fo) throws IOException {
420
this.lock = fo.lock();
422
this.delegate = fo.getOutputStream (this.lock);
424
if (this.delegate == null) {
425
this.lock.releaseLock();
430
public @Override void write(byte[] b, int off, int len) throws IOException {
431
this.delegate.write(b, off, len);
434
public @Override void write(byte[] b) throws IOException {
435
this.delegate.write(b);
438
public void write(int b) throws IOException {
439
this.delegate.write (b);
442
public @Override void close() throws IOException {
444
this.delegate.close();
446
this.lock.releaseLock();
447
synchronized (SourceFileObject.this) {
454
private class DocumentStream extends OutputStream {
456
private static final int BUF_SIZ=2048;
458
private final StyledDocument doc;
462
public DocumentStream (final StyledDocument doc) {
465
this.data = new byte[BUF_SIZ];
469
public synchronized @Override void write(byte[] b, int off, int len) throws IOException {
471
System.arraycopy(b,off,this.data,this.pos,len);
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);
481
public synchronized void write(int b) throws IOException {
483
this.data[this.pos++]=(byte)(b&0xff);
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) {
493
byte[] newData = new byte[newSize];
494
System.arraycopy(this.data,0,newData,0,this.pos);
499
public synchronized @Override void close() throws IOException {
501
NbDocument.runAtomic(this.doc,
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);
511
catch (UnsupportedEncodingException ee) {
512
ErrorManager.getDefault().notify (ee);
517
synchronized (SourceFileObject.this) {