/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc. All rights reserved. Use is
* subject to license terms.
*/
package org.jdesktop.swingbinding.adapters;
import java.beans.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.*;
import javax.swing.text.*;
import org.jdesktop.beansbinding.ext.BeanAdapterProvider;
/**
* @author Shannon Hickey
*/
public final class JTextComponentAdapterProvider implements BeanAdapterProvider {
private static final String PROPERTY_BASE = "text";
private static final String ON_ACTION_OR_FOCUS_LOST = PROPERTY_BASE + "_ON_ACTION_OR_FOCUS_LOST";
private static final String ON_FOCUS_LOST = PROPERTY_BASE + "_ON_FOCUS_LOST";
public final class Adapter extends BeanAdapterBase {
private JTextComponent component;
private Document document;
private boolean inDocumentListener;
private boolean installedFilter;
private String cachedText;
private Handler handler;
private Adapter(JTextComponent component, String property) {
super(property);
this.component = component;
}
public String getText() {
return component.getText();
}
public String getText_ON_ACTION_OR_FOCUS_LOST() {
return getText();
}
public String getText_ON_FOCUS_LOST() {
return getText();
}
public void setText(String text) {
component.setText(text);
component.setCaretPosition(0);
cachedText = text;
}
public void setText_ON_ACTION_OR_FOCUS_LOST(String text) {
setText(text);
}
public void setText_ON_FOCUS_LOST(String text) {
setText(text);
}
protected void listeningStarted() {
cachedText = component.getText();
handler = new Handler();
component.addPropertyChangeListener("document", handler);
if (property != PROPERTY_BASE) {
component.addFocusListener(handler);
}
if (property == ON_ACTION_OR_FOCUS_LOST && component instanceof JTextField) {
((JTextField)component).addActionListener(handler);
}
document = component.getDocument();
installDocumentListener();
}
protected void listeningStopped() {
cachedText = null;
component.removePropertyChangeListener("document", handler);
if (property != PROPERTY_BASE) {
component.removeFocusListener(handler);
}
if (property == ON_ACTION_OR_FOCUS_LOST && (component instanceof JTextField)) {
((JTextField)component).removeActionListener(handler);
}
uninstallDocumentListener();
document = null;
handler = null;
}
private void installDocumentListener() {
if (property != PROPERTY_BASE) {
return;
}
boolean useDocumentFilter = !(component instanceof JFormattedTextField);
if (useDocumentFilter && (document instanceof AbstractDocument) &&
((AbstractDocument)document).getDocumentFilter() == null) {
((AbstractDocument)document).setDocumentFilter(handler);
installedFilter = true;
} else {
document.addDocumentListener(handler);
installedFilter = false;
}
}
private void uninstallDocumentListener() {
if (property != PROPERTY_BASE) {
return;
}
if (installedFilter) {
AbstractDocument ad = (AbstractDocument)document;
if (ad.getDocumentFilter() == handler) {
ad.setDocumentFilter(null);
}
} else {
document.removeDocumentListener(handler);
}
}
private class Handler extends DocumentFilter implements ActionListener, DocumentListener,
FocusListener, PropertyChangeListener {
private void updateText() {
Object oldText = cachedText;
cachedText = getText();
firePropertyChange(oldText, cachedText);
}
private void documentTextChanged() {
try {
inDocumentListener = true;
textChanged();
} finally {
inDocumentListener = false;
}
}
private void textChanged() {
updateText();
}
public void propertyChange(PropertyChangeEvent pce) {
uninstallDocumentListener();
document = component.getDocument();
installDocumentListener();
updateText();
}
public void actionPerformed(ActionEvent e) {
updateText();
}
public void focusLost(FocusEvent e) {
if (!e.isTemporary()) {
updateText();
}
}
public void insertUpdate(DocumentEvent e) {
documentTextChanged();
}
public void removeUpdate(DocumentEvent e) {
documentTextChanged();
}
public void replace(DocumentFilter.FilterBypass fb, int offset,
int length, String text, AttributeSet attrs)
throws BadLocationException {
super.replace(fb, offset, length, text, attrs);
textChanged();
}
public void insertString(DocumentFilter.FilterBypass fb, int offset,
String string, AttributeSet attr) throws BadLocationException {
super.insertString(fb, offset, string, attr);
textChanged();
}
public void remove(DocumentFilter.FilterBypass fb, int offset, int length) throws BadLocationException {
super.remove(fb, offset, length);
textChanged();
}
public void focusGained(FocusEvent e) {}
public void changedUpdate(DocumentEvent e) {}
}
}
public boolean providesAdapter(Class<?> type, String property) {
if (!JTextComponent.class.isAssignableFrom(type)) {
return false;
}
property = property.intern();
return property == PROPERTY_BASE ||
property == ON_ACTION_OR_FOCUS_LOST ||
property == ON_FOCUS_LOST;
}
public Object createAdapter(Object source, String property) {
if (!providesAdapter(source.getClass(), property)) {
throw new IllegalArgumentException();
}
return new Adapter((JTextComponent)source, property);
}
public Class<?> getAdapterClass(Class<?> type) {
return JTextComponent.class.isAssignableFrom(type) ?
JTextComponentAdapterProvider.Adapter.class :
null;
}
}