/*
* PropertyDescriptorsHolder.java
*
* Created on 4 ������ 2006 �., 14:21
*
*/
package datechooser.beans.customizer;
import datechooser.beans.editor.BooleanEditorFoo;
import datechooser.beans.editor.StringEditor;
import java.beans.*;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import javax.swing.JOptionPane;
import static datechooser.beans.locale.LocaleUtils.getErrorsLocaleString;
/**
* Lets save / load beans properies.<br>
* �����, ����������� ���������������� ������ �� ���������� ���������.
* ������������ ����������� ������ ����������, �������� � ���������� �������.
* ������������ ������ ���� ������� ������� ��������� ���������.
* @author Androsov Vadim
* @since 1.0
*/
public class PropertyDescriptorsHolder implements PropertyChangeListener {
/**
* Constant "Operation completed successfully".<br>
* ��������� "�������� ��������� �������.
* @since 1.0
*/
public static final String OK = "ok";
private Map<String, PropertyEditorSupport> editorsCash;
private Map<String, PropertyDescriptor> descriptorsCash;
private PropertyDescriptor[] descriptorsCashArray;
private Map<Class, Class> additionalDescriptor;
private BeanInfo info;
private Object bean;
private boolean beanAttached;
private PropertyChangeSupport changeSupport;
/**
* Creates holder not linked with bean.<br>
* ������� ��������� �������, �� ��������� � ���������� �����������.
* @since 1.0
*/
public PropertyDescriptorsHolder(BeanInfo beanInfo) throws IntrospectionException {
this(beanInfo, null);
}
/**
* Creates holder linked with bean.<br>
* ������� ��������� �������, ��������� � ���������� �����������.
* @since 1.0
*/
public PropertyDescriptorsHolder(BeanInfo beanInfo, Object bean) throws IntrospectionException {
editorsCash = new HashMap<String, PropertyEditorSupport>();
descriptorsCash = new HashMap<String, PropertyDescriptor>();
changeSupport = new PropertyChangeSupport(this);
initializeAdditionalDescriptors();
setBeanInfo(beanInfo);
initialize(beanInfo);
prepareForBean(bean);
getBeanInfo().getPropertyDescriptors();
}
private void initializeAdditionalDescriptors() {
additionalDescriptor = new HashMap<Class, Class>();
additionalDescriptor.put(String.class, StringEditor.class);
additionalDescriptor.put(boolean.class, BooleanEditorFoo.class);
additionalDescriptor.put(Boolean.class, BooleanEditorFoo.class);
}
private Class getAdditionalDescriptorClass(Class key) {
return additionalDescriptor.get(key);
}
private void initialize(BeanInfo beanInfo) {
PropertyDescriptor[] descriptors = getBeanInfo().getPropertyDescriptors();
for (PropertyDescriptor elem : descriptors) {
Class editorClass = elem.getPropertyEditorClass();
if (editorClass == null) {
editorClass = getAdditionalDescriptorClass(elem.getReadMethod().getReturnType());
}
if (editorClass != null) {
createPropertyEditor(elem.getName(), editorClass);
registerDescriptor(elem);
}
}
descriptorsCashArray = descriptorsCash.values().toArray(new PropertyDescriptor[descriptorsCash.size()]);
}
/**
* Is descriptor instance of Boolean or boolean type.<br>
* ���������, ��������� �� ���������� � ����������� ���� (Boolean ��� boolean)
* @since 1.0
* @return True if descriptor is instance of boolean type.<br>
* ������, ���� ���������� ��������� ���������� ���.
* @param propertyDescriptor Descriptor to test.<br>
* ����������� ����������.
*/
public static boolean isBooleanDescriptor(PropertyDescriptor propertyDescriptor) {
Class type = propertyDescriptor.getReadMethod().getReturnType();
return (type == boolean.class) || (type == Boolean.class);
}
/**
* Is descriptor instance of String type.<br>
* ���������, ��������� �� ���������� � ���������� ����.
* @param propertyDescriptor Descriptor to test.<br>
* ����������� ����������.
* @return True if descriptor is instance of string type.<br>
* ������, ���� ���������� ��������� ������.
* @since 1.0
*/
public static boolean isStringDescriptor(PropertyDescriptor propertyDescriptor) {
Class type = propertyDescriptor.getReadMethod().getReturnType();
return type == String.class;
}
/**
* Prepares bean for customizing.<br>
* ������� ��������� � ���������.
* @since 1.0
*/
public void prepareForBean(Object bean) {
if (bean == null) return;
setBeanAttached(false);
testBean(bean);
this.bean = bean;
PropertyDescriptor[] descriptors = getPropertyDescriptors();
PropertyEditorSupport editor = null;
for (PropertyDescriptor elem : descriptors) {
try {
editor = getPropertyEditor(elem.getName());
editor.setValue(elem.getReadMethod().invoke(bean));
editor.firePropertyChange();
} catch (Exception ex) {
showExeption(ex);
}
}
setBeanAttached(true);
}
private void testBean(Object bean) {
String mustHaveInfo = bean.getClass().getName() + "BeanInfo";
if (!mustHaveInfo.equals(getBeanInfo().getClass().getName())) {
throw new RuntimeException("Incorrect Bean class name (" +
getBeanInfo().getClass().getName() + ')');
}
}
/**
* Get editor for specified property.<br>
* �������� �������� ��� ������� ��������.
* @since 1.0
* @param property Property name.<br>
* �������� ��������.
* @return Editor for specified property.<br>
* �������� ��� ������� ��������.
*/
public PropertyEditorSupport getPropertyEditor(String property) {
return editorsCash.get(property);
}
/**
* Get descriptor for specified property.<br>
* �������� �����-��������� ������� ��������.
* @param property Property name.<br>
* �������� ��������.
* @return Descriptor for specified property.<br>
* ��������� ��� ������� ��������.
* @since 1.0
*/
public PropertyDescriptor getPropertydescriptor(String property) {
return descriptorsCash.get(property);
}
private void registerDescriptor(PropertyDescriptor descr) {
descriptorsCash.put(descr.getName(), descr);
}
private void createPropertyEditor(String property, Class editorClass) {
if (editorsCash.containsKey(property)) {
return;
} else {
try {
PropertyEditorSupport newEditor;
newEditor = (PropertyEditorSupport)
Class.forName(editorClass.getName()).newInstance();
newEditor.addPropertyChangeListener(this);
editorsCash.put(property, newEditor);
return;
} catch (Exception ex) {
showExeption(ex);
}
}
}
private Object getProperty(String name) {
PropertyDescriptor descriptor = getPropertydescriptor(name);
try {
return descriptor.getReadMethod().invoke(bean);
} catch (Exception ex) {
showExeption(ex);
return null;
}
}
/**
* Updates bean when property was customized.<br>
* ��������� ���������, ����� ���� �������� ��� ���������.
* ���������� ����� �������� � ���������.
* @since 1.0
*/
public void propertyChange(PropertyChangeEvent evt) {
if (!isBeanAttached()) return;
PropertyDescriptor[] descriptors = getBeanInfo().getPropertyDescriptors();
PropertyEditorSupport editor = null;
for (PropertyDescriptor elem : descriptors) {
if (elem.getWriteMethod() == null) continue;
editor = getPropertyEditor(elem.getName());
if (evt.getSource() != editor) continue;
try {
elem.getWriteMethod().invoke(bean, editor.getValue());
firePropertyChange(elem.getName(), null, null);
} catch (Exception ex) {
setProperty(this.bean, elem.getName(), getProperty(elem.getName()));
showExeption(ex);
}
}
}
private void showExeption(Exception ex) {
Throwable cause = ex;
while (cause.getCause() != null) {
cause = cause.getCause();
}
JOptionPane.showMessageDialog(null,
"<html>" + cause.getMessage() + "<br>(<i>" + cause.getClass().getName() + "</i>)",
getErrorsLocaleString("Exception"), JOptionPane.ERROR_MESSAGE);
}
/**
* How many properties current bean has.<br>
* ������� ������� � ����������.
* @return Properties count.<br>
* ���������� �������.
*
* @since 1.0
*/
public int getPropertyCount() {
return descriptorsCash.size();
}
/**
* Get all property descriptors.<br>
* �������� ��� ������������������� ������� ������ PropertyDescriptor.
* @since 1.0
*/
public PropertyDescriptor[] getPropertyDescriptors() {
return descriptorsCashArray;
}
/**
* Is bean to customize.<br>
* ���� �� ��������� ��� ���������.
* @since 1.0
* @see datechooser.beans.customizer.PropertyDescriptorsHolder#prepareForBean(Object)
*/
public boolean isBeanAttached() {
return beanAttached;
}
private void setBeanAttached(boolean beanAttached) {
this.beanAttached = beanAttached;
}
/**
* Get object of BeanInfo class.<br>
* �������� ������ ������ BeanInfo.
* @since 1.0
*/
public BeanInfo getBeanInfo() {
return info;
}
/**
* Set object of BeanInfo class.<br>
* ���������� ������ ������ BeanInfo.
* @since 1.0
*/
public void setBeanInfo(BeanInfo info) {
this.info = info;
}
/**
* Write properties to file.<br>
* �������� �������� � ����.
* @since 1.0
*/
public String writeToFile(File file) {
try {
ObjectOutputStream dataOut = new ObjectOutputStream(
new BufferedOutputStream(
new FileOutputStream(file)));
PropertyPair[] pairs = new PropertyPair[editorsCash.size()];
int i = 0;
for (String elem : editorsCash.keySet()) {
pairs[i] = new PropertyPair(elem, editorsCash.get(elem).getValue());
++i;
}
dataOut.writeObject(pairs);
dataOut.flush();
dataOut.close();
return OK;
} catch (Exception ex) {
return ex.getClass().getName() + " : " + ex.getMessage();
}
}
private void setProperty(Object bean, String name, Object value) {
getPropertyEditor(name).setValue(value);
}
/**
* Read properties from input stream.
* Use if you plan to store properties file in jar<br>
* ��������� �������� �� ������ �����.
* ����������� ���� ���������� ������� ����� �������� � jar-�����.
* @since 1.0
*/
public String readFromStream(InputStream from) {
try {
ObjectInputStream dataIn = new ObjectInputStream(
new BufferedInputStream(from));
PropertyPair[] pairs = (PropertyPair[]) dataIn.readObject();
for (PropertyPair pair : pairs) {
setProperty(bean, pair.propertyName, pair.propertyValue);
}
dataIn.close();
return OK;
} catch (Exception ex) {
return ex.getClass().getName() + " : " + ex.getMessage();
}
}
/**
* Reads properies from file.<br>
* ������ �������� �� �����.
* @since 1.0
*/
public String readFromFile(File file) {
try {
return readFromStream(new FileInputStream(file));
} catch (FileNotFoundException ex) {
return ex.getClass().getName() + " : " + ex.getMessage();
}
}
/**
*
* @since 1.0
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
/**
*
* @since 1.0
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
/**
*
* @since 1.0
*/
public void firePropertyChange(String name, Object oldVal, Object newVal) {
changeSupport.firePropertyChange(name, oldVal, newVal);
}
}
class PropertyPair implements Serializable {
public PropertyPair(String propertyName, Object propertyValue) {
this.propertyName = propertyName;
this.propertyValue = propertyValue;
}
String propertyName;
Object propertyValue;
}