package automenta.vivisect.swing.property;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Dialog.ModalityType;
import java.awt.Frame;
import java.awt.Window;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyEditor;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import automenta.vivisect.swing.property.propertysheet.PropertySheet;
import automenta.vivisect.swing.property.propertysheet.PropertySheetDialog;
import automenta.vivisect.swing.property.propertysheet.PropertySheetPanel;
import automenta.vivisect.swing.property.swing.BannerPanel;
public class PropertyUtils {
private static SerializableProperty createProperty(Object obj, Field f,
boolean forEdit) {
Property a = f.getAnnotation(Property.class);
if (a != null) {
f.setAccessible(true);
String name = f.getName();
String displayName = a.name();
String desc = a.description();
Class<? extends PropertyEditor> editClass = null;
String category = a.category();
if (a.name().length() == 0)
displayName = f.getName();
if (a.description().length() == 0) {
desc = displayName;
}
if (a.editorClass() != PropertyEditor.class) {
editClass = a.editorClass();
}
if (category == null || category.length() == 0) {
category = "Base";
}
Object o = null;
try {
o = f.get(null);
} catch (Exception e) {
// nothing
}
if (o == null) {
try {
o = f.get(obj);
} catch (Exception e) {
// nothing
}
}
SerializableProperty pp = new SerializableProperty(name,
f.getType(), o);
pp.setShortDescription(desc);
pp.setEditable(a.editable());
pp.setDisplayName(displayName);
pp.setEditor(editClass);
if (category != null && category.length() > 0) {
pp.setCategory(category);
}
return pp;
}
return null;
}
private static Field[] getFields(Object o) {
Class<?> c;
if (o instanceof Class<?>)
c = (Class<?>) o;
else
c = o.getClass();
HashSet<Field> fields = new HashSet<>();
while (c != Object.class) {
for (Field f : c.getFields())
fields.add(f);
for (Field f : c.getDeclaredFields()) {
f.setAccessible(true);
fields.add(f);
}
c = c.getSuperclass();
}
return fields.toArray(new Field[0]);
}
public static LinkedHashMap<String, SerializableProperty> getProperties(
Object obj, boolean editable) {
LinkedHashMap<String, SerializableProperty> props = new LinkedHashMap<String, SerializableProperty>();
for (Field f : getFields(obj)) {
SerializableProperty pp = createProperty(obj, f, editable);
if (pp != null)
props.put(f.getName(), pp);
}
return props;
}
public static void setProperties(Object obj,
LinkedHashMap<String, SerializableProperty> props) {
setProperties(obj, props, true);
}
public static void setProperties(Object obj,
LinkedHashMap<String, SerializableProperty> props,
boolean triggerEvents) {
Class<? extends Object> providerClass = obj instanceof Class<?> ? (Class<?>) obj
: obj.getClass();
String name;
SerializableProperty property;
Object propertyValue;
for (Field f : getFields(providerClass)) {
Property a = f.getAnnotation(Property.class);
if (a == null)
continue;
name = f.getName();
property = props.get(name);
if (property == null) {
Logger.getGlobal().log(Level.WARNING,
"Property " + name + " will not be saved.");
continue;
}
try {
propertyValue = property.getValue();
Object oldValue = f.get(obj);
try {
f.set(obj, propertyValue);
} catch (Exception e) {
switch (f.getGenericType().toString()) {
case "int":
case "Integer":
f.setInt(obj, (int) Double.parseDouble(propertyValue
.toString()));
propertyValue = (int) Double.parseDouble(propertyValue
.toString());
break;
case "long":
case "Long":
f.setLong(obj, (long) Double.parseDouble(propertyValue
.toString()));
propertyValue = (long) Double.parseDouble(propertyValue
.toString());
break;
case "short":
case "Short":
f.setShort(obj, (short) Double
.parseDouble(propertyValue.toString()));
propertyValue = (short) Double
.parseDouble(propertyValue.toString());
break;
case "byte":
case "Byte":
f.setByte(obj, (byte) Double.parseDouble(propertyValue
.toString()));
propertyValue = (byte) Double.parseDouble(propertyValue
.toString());
break;
case "float":
case "Float":
f.setFloat(obj, (float) Double
.parseDouble(propertyValue.toString()));
propertyValue = (float) Double
.parseDouble(propertyValue.toString());
break;
case "double":
case "Double":
f.setDouble(obj,
Double.parseDouble(propertyValue.toString()));
break;
default:
break;
}
}
if (triggerEvents && !oldValue.equals(propertyValue)
&& obj instanceof PropertyChangeListener)
((PropertyChangeListener) obj)
.propertyChange(new PropertyChangeEvent(
PropertyUtils.class, f.getName(), oldValue,
propertyValue));
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
public static void saveProperties(Object obj, File f) throws IOException {
LinkedHashMap<String, SerializableProperty> props = getProperties(obj,
true);
Properties p = new Properties();
for (SerializableProperty prop : props.values())
p.setProperty(prop.getName(), prop.toString());
p.store(new FileWriter(f), "Properties saved on " + new Date());
}
public static String saveProperties(Object obj) throws IOException {
LinkedHashMap<String, SerializableProperty> props = getProperties(obj,
true);
Properties p = new Properties();
for (SerializableProperty prop : props.values())
p.setProperty(prop.getName(), prop.toString());
StringWriter writer = new StringWriter();
p.store(writer, null);
return writer.toString().replaceAll("^\\#.*", "").trim()+"\n";
}
public static void setProperties(Object obj, Properties p,
boolean triggerEvents) {
LinkedHashMap<String, SerializableProperty> props = getProperties(obj,
true);
for (Entry<Object, Object> entry : p.entrySet()) {
if (props.containsKey(entry.getKey())) {
SerializableProperty sp = props.get(entry.getKey());
sp.fromString("" + entry.getValue());
}
}
setProperties(obj, props, triggerEvents);
}
public static void loadProperties(Object obj, String properties,
boolean triggerEvents) throws IOException {
Properties p = new Properties();
StringReader reader = new StringReader(properties);
p.load(reader);
setProperties(obj, p, triggerEvents);
}
public static void loadProperties(Object obj, File f) throws IOException {
loadProperties(obj, f, true);
}
public static void loadProperties(Object obj, File f, boolean triggerEvents)
throws IOException {
Properties p = new Properties();
p.load(new FileReader(f));
setProperties(obj, p, triggerEvents);
}
public static PropertySheetPanel getPropsPanel(Object obj, boolean editable) {
PropertySheetPanel psp = new PropertySheetPanel();
psp.setMode(PropertySheet.VIEW_AS_CATEGORIES);
psp.setToolBarVisible(false);
psp.setEnabled(true);
psp.setSortingCategories(true);
psp.setDescriptionVisible(true);
Collection<SerializableProperty> props = getProperties(obj, editable)
.values();
for (SerializableProperty p : props) {
p.setEditable(editable && p.isEditable());
psp.addProperty(p);
}
return psp;
}
public static void editProperties(Window parent, Object obj,
boolean editable) {
final PropertySheetPanel psp = getPropsPanel(obj, editable);
final PropertySheetDialog propertySheetDialog = createWindow(parent,
editable, psp, "Properties of "
+ obj.getClass().getSimpleName());
if (!propertySheetDialog.ask()) {
// cancelled
return;
}
LinkedHashMap<String, SerializableProperty> newProps = new LinkedHashMap<>();
for (automenta.vivisect.swing.property.propertysheet.Property p : psp.getProperties())
newProps.put(p.getName(), new SerializableProperty(p));
setProperties(obj, newProps, true);
}
public static PropertySheetDialog createWindow(Window parent,
boolean editable, final PropertySheetPanel psp, String title) {
final PropertySheetDialog propertySheetDialog;
if (parent instanceof Dialog) {
Dialog pDialog = (Dialog) parent;
propertySheetDialog = new PropertySheetDialog(pDialog);
} else if (parent instanceof Frame) {
Frame pFrame = (Frame) parent;
propertySheetDialog = new PropertySheetDialog(pFrame);
} else {
propertySheetDialog = new PropertySheetDialog() {
private static final long serialVersionUID = 1L;
@Override
public void ok() {
if (psp.getTable().getEditorComponent() != null)
psp.getTable().commitEditing();
super.ok();
};
};
}
if (editable) {
propertySheetDialog
.setDialogMode(PropertySheetDialog.OK_CANCEL_DIALOG);
} else {
propertySheetDialog.setDialogMode(PropertySheetDialog.CLOSE_DIALOG);
}
int sb = 1;
for (Component compL0 : propertySheetDialog.getRootPane()
.getComponents()) {
if (compL0 instanceof JLayeredPane) {
for (Component compL01 : ((JLayeredPane) compL0)
.getComponents()) {
if (!(compL01 instanceof JPanel))
continue;
for (Component compL1 : ((JPanel) compL01).getComponents()) {
if (compL1 instanceof BannerPanel)
continue;
if (compL1 instanceof JPanel) {
for (Component compL2 : ((JPanel) compL1)
.getComponents()) {
for (Component compL3 : ((JPanel) compL2)
.getComponents()) {
if (compL3 instanceof JButton) {
if (propertySheetDialog.getDialogMode() == PropertySheetDialog.OK_CANCEL_DIALOG
&& sb == 1) {
((JButton) compL3).setText("OK");
sb--;
} else if (propertySheetDialog
.getDialogMode() == PropertySheetDialog.CLOSE_DIALOG
|| sb == 0) {
((JButton) compL3)
.setText(propertySheetDialog
.getDialogMode() == PropertySheetDialog.CLOSE_DIALOG ? "Close"
: "Cancel");
sb--;
break;
}
if (sb < 0)
break;
}
}
if (sb < 0)
break;
}
}
}
}
}
}
if (title != null) {
propertySheetDialog.getBanner().setTitle(title);
propertySheetDialog.setTitle(title);
}
// propertySheetDialog.setIconImage(ImageUtils.getImage("images/menus/settings.png"));
// propertySheetDialog.getBanner().setIcon(ImageUtils.getIcon("images/settings.png"));
propertySheetDialog.getContentPane().add(psp);
propertySheetDialog.pack();
propertySheetDialog.setLocationRelativeTo(null);
propertySheetDialog.setModalityType(ModalityType.DOCUMENT_MODAL);
return propertySheetDialog;
}
}