/*
Copyright (c) 2014 Wolfgang Imig
This file is part of the library "Java Add-in for Microsoft Office".
This file must be used according to the terms of
MIT License, http://opensource.org/licenses/MIT
*/
package com.wilutions.itol;
import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.json.JSONObject;
import com.wilutions.com.ComEnum;
import com.wilutions.com.ComException;
import com.wilutions.com.reg.DeclRegistryValue;
import com.wilutions.itol.db.Default;
/**
* This class helps to store and load the state of UI controls.
*/
@Deprecated
public class UserProfile {
private static final int OPT_ONLY_ANNOTATED_FIELDS = 1;
private static final int OPT_ALL_FIELDS = 0;
private String appName;
private String manufacturerName;
private JSONObject root = new JSONObject();
/**
* Initialize the registry destination path.
*
* @param manufacturerName
* Manufacturer name
* @param appName
* Application name
*/
private UserProfile(String manufacturerName, String appName) {
this.appName = appName;
this.manufacturerName = manufacturerName;
}
public static UserProfile readFromAppData(String manufacturerName, String appName) {
UserProfile ret = null;
try {
File configFile = getConfigFile(manufacturerName, appName);
System.out.println("Load user profile from " + configFile);
ret = read(configFile, manufacturerName, appName);
}
catch(Exception e) {
System.out.println("File not found.");
ret = new UserProfile(manufacturerName, appName);
}
return ret;
}
private void flush() {
try {
writeIntoAppData();
} catch (Exception e) {
e.printStackTrace();
}
}
private void writeIntoAppData() throws Exception {
File configFile = getConfigFile(manufacturerName, appName);
System.out.println("Write config into " + configFile);
write(configFile);
}
private static UserProfile read(File configFile, String manufacturerName, String appName) throws Exception {
String json = new String(Files.readAllBytes(configFile.toPath()), "UTF-8");
UserProfile userProfile = new UserProfile(manufacturerName, appName);
userProfile.root = new JSONObject(json);
return userProfile;
}
private void write(File configFile) throws Exception {
String json = root.toString(2);
configFile.delete();
Files.write(configFile.toPath(), json.getBytes("UTF-8"), StandardOpenOption.CREATE);
}
/**
* Store all fields annotated by {@link DeclRegistryValue}.
*
* @param obj
* Store fields of this object.
*/
public void writeFields(Object obj) {
if (obj == null) throw new IllegalArgumentException("obj must not be null");
String subKey = obj.getClass().getName();
writeObject(subKey, obj, OPT_ONLY_ANNOTATED_FIELDS);
flush();
}
/**
* Read all fields annotated by {@link DeclRegistryValue}.
*
* @param obj
* Read annotated fields of this object.
*/
public void readFields(Object obj) {
String subKey = obj.getClass().getName();
readFields(subKey, obj, OPT_ONLY_ANNOTATED_FIELDS);
}
/**
* Read an object or primitive value at the given sub-key. This function
* reads all fields - not only annotated fields.
*
* @param subKey
* Sub-key
* @return Object or primitive value
*/
public Object read(String subKey) {
return readObject(subKey);
}
/**
* Write object or primitive value at the given sub-key. This function
* writes all fields - not only annotated fields.
*
* @param subKey
* Sub-key
* @param obj
* Object to be written.
*/
public void write(String subKey, Object obj) {
String destKey = subKey;
if (obj != null) {
writeObject(destKey, obj, OPT_ALL_FIELDS);
}
else {
deleteObject(destKey);
}
flush();
}
private void readFields(String key, Object obj, int opts) {
Class<?> clazz = obj.getClass();
while (clazz != null && clazz != Object.class) {
for (Field field : clazz.getDeclaredFields()) {
String fieldName = field.getName();
int mods = field.getModifiers();
if (Modifier.isStatic(mods)) continue;
if (Modifier.isFinal(mods)) continue;
if (Modifier.isTransient(mods)) continue;
if ((opts & OPT_ONLY_ANNOTATED_FIELDS) != 0) {
DeclRegistryValue regValueAnno = field.getAnnotation(DeclRegistryValue.class);
if (regValueAnno == null) continue;
String s = regValueAnno.value();
if (s != null && s.length() != 0) {
fieldName = s;
}
}
if (!Modifier.isPublic(mods)) {
field.setAccessible(true);
}
try {
Object fieldValue = getFieldValue(key, fieldName, field.getType());
if (fieldValue != null) {
field.set(obj, fieldValue);
}
}
catch (Throwable ignored) {
}
}
clazz = clazz.getSuperclass();
}
}
private Object readObject(String key) {
String className = (String) RegUtil_getRegistryValue(key, "", "");
if (className == null || className.length() == 0) return null;
Object ret = null;
try {
Class<?> clazz = Class.forName(className);
// Value written as object?
if (clazz.isArray() || (clazz == String.class) || (clazz == Integer.class) || (clazz == Long.class)
|| (clazz == Double.class) || (clazz == Float.class) || (clazz == Boolean.class) || (clazz.isEnum())
|| (ComEnum.class.isAssignableFrom(clazz)) || (List.class.isAssignableFrom(clazz))
|| (Map.class.isAssignableFrom(clazz))) {
return getFieldValue(key, "value", clazz);
}
ret = clazz.newInstance();
readFields(key, ret, OPT_ALL_FIELDS);
}
catch (Throwable e) {
e.printStackTrace();
}
return ret;
}
private Object getFieldValueArray(String key, String fieldName, Class<?> arrayType)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
String arrayKey = key + "\\" + fieldName;
Class<?> elementType = arrayType.getComponentType();
int length = Integer.valueOf((String) RegUtil_getRegistryValue(arrayKey, "length", "0"));
Object fieldValue = Array.newInstance(elementType, length);
for (int i = 0; i < length; i++) {
Object elementValue = getFieldValue(arrayKey, Integer.toString(i), elementType);
Array.set(fieldValue, i, elementValue);
}
return fieldValue;
}
private void setFieldValueArray(String key, String fieldName, Object arrayValue) throws ComException {
String arrayKey = key + "\\" + fieldName;
int length = Array.getLength(arrayValue);
RegUtil_setRegistryValue(arrayKey, "length", length);
for (int i = 0; i < length; i++) {
Object elementValue = Array.get(arrayValue, i);
if (elementValue == null) continue;
setFieldValue(arrayKey, Integer.toString(i), elementValue, elementValue.getClass());
}
}
private Object getFieldValueList(String key, String fieldName, Class<?> listType)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
String listKey = key + "\\" + fieldName;
Integer length = (Integer) RegUtil_getRegistryValue(listKey, "length", 0);
@SuppressWarnings("unchecked")
List<Object> fieldValue = listType.equals(List.class) ? new ArrayList<Object>()
: (List<Object>) listType.newInstance();
if (length != null && length != 0) {
String elementTypeName = (String) RegUtil_getRegistryValue(listKey, "elementClass", "");
Class<?> elementType = Class.forName(elementTypeName);
for (int i = 0; i < length; i++) {
Object elementValue = getFieldValue(listKey, Integer.toString(i), elementType);
if (elementValue != null) {
fieldValue.add(elementValue);
}
}
}
return fieldValue;
}
@SuppressWarnings("rawtypes")
private void setFieldValueList(String key, String fieldName, Object listValue) throws ComException {
String listKey = key + "\\" + fieldName;
List list = (List) listValue;
int length = list.size();
RegUtil_setRegistryValue(listKey, "length", length);
int i = 0;
boolean classWritten = false;
for (Object elementValue : list) {
if (!classWritten && elementValue != null) {
RegUtil_setRegistryValue(listKey, "elementClass", elementValue.getClass().getName());
classWritten = true;
}
setFieldValue(listKey, Integer.toString(i++), elementValue, elementValue.getClass());
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private Object getFieldValueMap(String key, String fieldName, Class<?> mapType)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
String mapKey = key + "\\" + fieldName;
Integer length = (Integer) RegUtil_getRegistryValue(mapKey, "length", 0);
Map fieldValue = mapType.equals(Map.class) ? new HashMap<Object, Object>() : (Map) mapType.newInstance();
if (length != null && length != 0) {
String keyTypeName = (String) RegUtil_getRegistryValue(mapKey, "keyClass", "");
String valueTypeName = (String) RegUtil_getRegistryValue(mapKey, "valueClass", "");
Class<?> keyType = Class.forName(keyTypeName);
Class<?> valueType = Class.forName(valueTypeName);
for (int i = 0; i < length; i++) {
String elementKey = mapKey + "\\" + i;
Object keyValue = getFieldValue(elementKey, "key", keyType);
if (keyValue != null) {
Object valueValue = getFieldValue(elementKey, "value", valueType);
if (valueValue != null) {
fieldValue.put(keyValue, valueValue);
}
}
}
}
return fieldValue;
}
@SuppressWarnings("rawtypes")
private void setFieldValueMap(String key, String fieldName, Object mapValue) throws ComException {
String mapKey = key + "\\" + fieldName;
Map map = (Map) mapValue;
int length = map.size();
RegUtil_setRegistryValue(mapKey, "length", length);
boolean keyClassWritten = false, valueClassWritten = false;
int i = 0;
for (Object e : map.entrySet()) {
String elementKey = mapKey + "\\" + (i++);
Map.Entry mapEntry = (Map.Entry) e;
Object keyValue = mapEntry.getKey();
Object valueValue = mapEntry.getValue();
if (!keyClassWritten && keyValue != null) {
RegUtil_setRegistryValue(mapKey, "keyClass", keyValue.getClass().getName());
}
if (!valueClassWritten && valueValue != null) {
RegUtil_setRegistryValue(mapKey, "valueClass", valueValue.getClass().getName());
}
setFieldValue(elementKey, "key", keyValue, keyValue.getClass());
setFieldValue(elementKey, "value", valueValue, valueValue.getClass());
}
}
private Object getFieldValue(String key, String fieldName, Class<?> fieldType)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Object ret = null;
if (fieldType.isArray()) {
ret = getFieldValueArray(key, fieldName, fieldType);
}
else if (fieldType == String.class) {
ret = RegUtil_getRegistryValue(key, fieldName, "");
}
else if (fieldType == Integer.class || fieldType == int.class) {
Object regValue = RegUtil_getRegistryValue(key, fieldName, null);
if (regValue != null) {
try {
ret = (Integer)regValue;
}
catch (Exception ignored) {}
}
}
else if (fieldType == Long.class || fieldType == long.class) {
Object regValue = RegUtil_getRegistryValue(key, fieldName, null);
if (regValue != null) {
try {
ret = Long.valueOf((String) regValue);
}
catch (Exception ignored) {}
}
}
else if (fieldType == Double.class || fieldType == double.class) {
Object regValue = RegUtil_getRegistryValue(key, fieldName, null);
if (regValue != null) {
try {
ret = Double.valueOf((String) regValue);
}
catch (Exception ignored) {}
}
}
else if (fieldType == Float.class || fieldType == float.class) {
Object regValue = RegUtil_getRegistryValue(key, fieldName, null);
if (regValue != null) {
try {
ret = Float.valueOf((String) regValue);
}
catch (Exception ignored) {}
}
}
else if (fieldType == Boolean.class || fieldType == boolean.class) {
Object regValue = RegUtil_getRegistryValue(key, fieldName, Boolean.FALSE.toString());
if (regValue != null) {
try {
ret = Boolean.valueOf((String) regValue);
}
catch (Exception ignored) {}
}
}
else if (fieldType.isEnum()) {
String enumValueStr = (String) RegUtil_getRegistryValue(key, fieldName, "");
for (Object e : fieldType.getEnumConstants()) {
if (e.toString().equals(enumValueStr)) {
ret = e;
break;
}
}
}
else if (ComEnum.class.isAssignableFrom(fieldType)) {
String enumValueStr = (String) RegUtil_getRegistryValue(key, fieldName, "");
if (!enumValueStr.isEmpty()) {
try {
int enumValue = Integer.parseInt(enumValueStr);
Method m = fieldType.getDeclaredMethod("valueOf", int.class);
ret = m.invoke(null, enumValue);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
else if (List.class.isAssignableFrom(fieldType)) {
ret = getFieldValueList(key, fieldName, fieldType);
}
else if (Map.class.isAssignableFrom(fieldType)) {
ret = getFieldValueMap(key, fieldName, fieldType);
}
else {
ret = readObject(key + "\\" + fieldName);
}
return ret;
}
private void setFieldValue(String key, String fieldName, Object value, Class<?> fieldType)
throws ComException {
if (value == null) {
RegUtil_deleteRegistryValue(key, fieldName);
}
else {
if (fieldType.isArray()) {
setFieldValueArray(key, fieldName, value);
}
else if (fieldType == String.class) {
RegUtil_setRegistryValue(key, fieldName, (String) value);
}
else if (fieldType == Integer.class || fieldType == int.class) {
RegUtil_setRegistryValue(key, fieldName, (Integer) value);
}
else if (fieldType == Long.class || fieldType == long.class) {
RegUtil_setRegistryValue(key, fieldName, value.toString());
}
else if (fieldType == Double.class || fieldType == double.class) {
RegUtil_setRegistryValue(key, fieldName, value.toString());
}
else if (fieldType == Float.class || fieldType == float.class) {
RegUtil_setRegistryValue(key, fieldName, value.toString());
}
else if (fieldType == Boolean.class || fieldType == boolean.class) {
RegUtil_setRegistryValue(key, fieldName, value.toString());
}
else if (fieldType.isEnum()) {
RegUtil_setRegistryValue(key, fieldName, value.toString());
}
else if (ComEnum.class.isAssignableFrom(fieldType)) {
try {
Field f = fieldType.getDeclaredField("value");
String enumValueStr = Integer.toString((int) f.get(value));
RegUtil_setRegistryValue(key, fieldName, enumValueStr);
}
catch (Exception e) {
e.printStackTrace();
}
}
else if (List.class.isAssignableFrom(fieldType)) {
setFieldValueList(key, fieldName, value);
}
else if (Map.class.isAssignableFrom(fieldType)) {
setFieldValueMap(key, fieldName, value);
}
else {
writeObject(key + "\\" + fieldName, value, OPT_ALL_FIELDS);
}
}
}
private void writeObject(String key, Object obj, int opts) {
Class<?> clazz = obj != null ? obj.getClass() : null;
String className = clazz != null ? clazz.getName() : "";
try {
RegUtil_setRegistryValue(key, "", className);
if (className == null || className.length() == 0) return;
if (clazz.isArray() || (clazz == String.class) || (clazz == Integer.class) || (clazz == Long.class)
|| (clazz == Double.class) || (clazz == Float.class) || (clazz == Boolean.class) || (clazz.isEnum())
|| (ComEnum.class.isAssignableFrom(clazz)) || (List.class.isAssignableFrom(clazz))
|| (Map.class.isAssignableFrom(clazz))) {
setFieldValue(key, "value", obj, clazz);
return;
}
while (clazz != null && clazz != Object.class) {
for (Field field : clazz.getDeclaredFields()) {
String fieldName = field.getName();
int mods = field.getModifiers();
if (Modifier.isStatic(mods)) continue;
if (Modifier.isFinal(mods)) continue;
if (Modifier.isTransient(mods)) continue;
if ((opts & OPT_ONLY_ANNOTATED_FIELDS) != 0) {
DeclRegistryValue regValueAnno = field.getAnnotation(DeclRegistryValue.class);
if (regValueAnno == null) continue;
String s = regValueAnno.value();
if (s != null && s.length() != 0) {
fieldName = s;
}
}
if (!Modifier.isPublic(mods)) {
field.setAccessible(true);
}
try {
Object fieldValue = field.get(obj);
Class<?> fieldClass = field.getType();
setFieldValue(key, fieldName, fieldValue, fieldClass);
}
catch (Throwable e) {
e.printStackTrace();
}
}
clazz = clazz.getSuperclass();
}
}
catch (Throwable e) {
}
}
private static class Property {
@DeclRegistryValue
String id;
@DeclRegistryValue
int type = 3;
@DeclRegistryValue
Object value;
Property(String id, Object value) {
this.id = id;
this.value = value;
};
@SuppressWarnings("unused")
Property() {
};
public String toString() {
return "[" + id + "," + value + "," + type + "]";
}
}
private static class Globals {
@DeclRegistryValue
List<Property> properties;
public String toString() {
return "[" + properties + "]";
}
}
private static File getConfigFile(String manufacturerName, String appName) {
String appData = System.getenv("APPDATA");
if (Default.value(appData).isEmpty()) {
appData = ".";
}
File dataDir = new File(new File(new File(appData), manufacturerName), appName).getAbsoluteFile();
dataDir.mkdirs();
File configFile = new File(dataDir, "user-profile.json");
return configFile;
}
private void deleteObject(String key) {
RegUtil_purgeRegistryKey(key);
}
private JSONObject getAtPath(String key) {
String[] keys = new String[0];
try {
keys = key.split("\\\\");
}
catch (Exception e) {
e.printStackTrace();
}
JSONObject jsobj = root;
for (int i = 0; i < keys.length; i++) {
JSONObject elm = null;
if (jsobj.has(keys[i])) {
elm = jsobj.getJSONObject(keys[i]);
}
else {
elm = new JSONObject();
jsobj.put(keys[i], elm);
}
jsobj = elm;
}
return jsobj;
}
private void RegUtil_deleteRegistryValue(String key, String valueName) {
valueName = makeValueName(valueName);
JSONObject jsobj = getAtPath(key);
jsobj.remove(valueName);
}
private void RegUtil_purgeRegistryKey(String key) {
String[] keys = key.split("\\\\");
JSONObject jsobj = root;
for (int i = 0; i < keys.length-1; i++) {
JSONObject elm = null;
if (jsobj.has(keys[i])) {
elm = jsobj.getJSONObject(keys[i]);
}
else {
elm = new JSONObject();
jsobj.put(keys[i], elm);
}
jsobj = elm;
}
String keyToRemove = keys[keys.length-1];
if (jsobj.has(keyToRemove)) {
jsobj.remove(keyToRemove);
}
}
private Object RegUtil_getRegistryValue(String key, String valueName, Object defaultValue) {
Object ret = defaultValue;
valueName = makeValueName(valueName);
JSONObject jsobj = getAtPath(key);
if (jsobj.has(valueName)) {
ret = jsobj.get(valueName);
}
return ret;
}
private void RegUtil_setRegistryValue(String key, String valueName, Object value) {
valueName = makeValueName(valueName);
JSONObject jsobj = getAtPath(key);
jsobj.put(valueName, value);
}
private String makeValueName(String valueName) {
if (Default.value(valueName).isEmpty()) {
valueName = "(_type)";
}
return valueName;
}
public static void main(String[] args) {
UserProfile registry = new UserProfile("WILUTIONS", "TEST-REGISTRY");
Globals globals = new Globals();
globals.properties = Arrays.asList(new Property("abc", "def"));
System.out.println("globals=" + globals);
registry.writeFields(globals);
Globals rglobals = new Globals();
registry.readFields(rglobals);
System.out.println("rglobals=" + rglobals);
}
}