/*
* Copyright (c) 2012 Sam Harwell, Tunnel Vision Laboratories LLC
* All rights reserved.
*
* The source code of this document is proprietary work, and is not licensed for
* distribution. For information about licensing, contact Sam Harwell at:
* sam@tunnelvisionlabs.com
*/
package org.antlr.netbeans.editor.text.impl;
import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import org.antlr.netbeans.editor.text.DocumentSnapshot;
import org.antlr.netbeans.editor.text.VersionedDocument;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;
import org.netbeans.api.queries.FileEncodingQuery;
import org.netbeans.editor.BaseDocument;
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
import org.netbeans.modules.editor.NbEditorUtilities;
import org.openide.filesystems.FileObject;
import org.openide.util.Parameters;
/**
*
* @author Sam Harwell
*/
public class NbVersionedDocument implements VersionedDocument {
// -J-Dorg.antlr.netbeans.editor.text.impl.NbVersionedDocument.level=FINE
private static final Logger LOGGER = Logger.getLogger(NbVersionedDocument.class.getName());
private static final WeakReference<NbDocumentVersion> NullVersion = new WeakReference<>(null);
@NullAllowed
private final BaseDocument document;
@NullAllowed
private final FileObject fileObject;
@NonNull
private final Map<Object, Object> properties = new HashMap<>();
@NonNull
private NbNormalizedDocumentChangeCollection pendingChanges = new NbNormalizedDocumentChangeCollection();
private Reference<NbDocumentVersion> latestVersion = NullVersion;
private int latestVersionNumber = 0;
public NbVersionedDocument(@NonNull BaseDocument document) {
Parameters.notNull("document", document);
this.document = document;
document.addDocumentListener(new Listener());
this.fileObject = null;
}
public NbVersionedDocument(@NonNull FileObject fileObject) {
Parameters.notNull("fileObject", fileObject);
this.document = null;
this.fileObject = fileObject;
}
@Override
public BaseDocument getDocument() {
return document;
}
@Override
public DocumentSnapshot getCurrentSnapshot() {
return applyChanges().getSnapshot();
}
@Override
public String getMimeType() {
if (fileObject != null) {
return fileObject.getMIMEType();
}
assert document != null;
return DocumentUtilities.getMimeType(document);
}
@Override
public FileObject getFileObject() {
if (fileObject != null) {
return fileObject;
}
assert document != null;
return NbEditorUtilities.getFileObject(document);
}
@Override
public Object getProperty(Object key) {
synchronized (properties) {
return properties.get(key);
}
}
@Override
public Object putProperty(Object key, Object value) {
synchronized (properties) {
return properties.put(key, value);
}
}
private @NonNull NbDocumentVersion applyChanges() {
if (document == null) {
assert fileObject != null;
assert pendingChanges.isEmpty();
NbDocumentVersion version = latestVersion.get();
if (version != null) {
return version;
}
try {
String text;
Charset charset = FileEncodingQuery.getEncoding(fileObject);
if (charset != null) {
text = fileObject.asText(charset.name());
} else {
text = fileObject.asText();
}
version = new NbDocumentVersion(this, latestVersionNumber + 1, new LineTextCache(text));
latestVersion = new SoftReference<>(version);
latestVersionNumber = version.getVersionNumber();
pendingChanges = new NbNormalizedDocumentChangeCollection();
return version;
} catch (IOException ex) {
LOGGER.log(Level.WARNING, "An exception occurred while tracking versioned document changes.", ex);
throw new UnsupportedOperationException(ex);
}
}
assert document != null;
document.readLock();
try {
synchronized (this) {
NbDocumentVersion version = latestVersion.get();
if (pendingChanges.isEmpty() && version != null) {
return version;
}
if (version == null) {
try {
version = new NbDocumentVersion(this, latestVersionNumber + 1, new LineTextCache(document.getText(0, document.getLength())));
} catch (BadLocationException ex) {
LOGGER.log(Level.WARNING, "An exception occurred while tracking versioned document changes.", ex);
throw new IllegalStateException("Shouldn't be reachable.", ex);
}
} else if (true) {
version = version.translate(pendingChanges);
}
latestVersion = new WeakReference<>(version);
latestVersionNumber = version.getVersionNumber();
pendingChanges = new NbNormalizedDocumentChangeCollection();
return version;
}
} finally {
document.readUnlock();
}
}
private void addPendingChange(@NonNull NbDocumentChange change) {
Parameters.notNull("change", change);
pendingChanges.add(change);
}
private class Listener implements DocumentListener {
@Override
public void insertUpdate(DocumentEvent e) {
if (latestVersion.get() == null) {
return;
}
int offset = e.getOffset();
String text = DocumentUtilities.getModificationText(e);
NbDocumentChange textChange = new NbDocumentChange(offset, "", offset, text);
addPendingChange(textChange);
}
@Override
public void removeUpdate(DocumentEvent e) {
if (latestVersion.get() == null) {
return;
}
int offset = e.getOffset();
String text = DocumentUtilities.getModificationText(e);
NbDocumentChange textChange = new NbDocumentChange(offset, text, offset, "");
addPendingChange(textChange);
}
@Override
public void changedUpdate(DocumentEvent e) {
if (latestVersion.get() == null) {
return;
}
LOGGER.log(Level.WARNING, "Change events are not yet supported.");
}
}
}