package org.objectstyle.wolips.baseforuiplugins.plist;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.objectstyle.woenvironment.plist.ParserDataStructureFactory;
import org.objectstyle.woenvironment.plist.PropertyListParserException;
import org.objectstyle.woenvironment.plist.WOLPropertyListSerialization;
import org.objectstyle.wolips.baseforplugins.util.ComparisonUtils;
public class PropertyListPath {
public static enum Type {
String("String", String.class, null), Number("Number", Number.class), Date("Date", Date.class, Calendar.class), Boolean("Boolean", Boolean.class), Array("Array", List.class, Set.class), Dictionary("Dictionary", Map.class), Data("Data", Object.class);
private String _name;
private Class[] _types;
private Type(String name, Class... types) {
_name = name;
_types = types;
}
public String getName() {
return _name;
}
public Class[] getTypes() {
return _types;
}
@SuppressWarnings("unchecked")
public boolean matches(Object obj) {
Class objType = (obj == null) ? null : obj.getClass();
for (Class type : _types) {
if (type == null) {
if (objType == null) {
return true;
}
} else if (objType != null) {
if (type.isAssignableFrom(objType)) {
return true;
}
}
}
return false;
}
}
private ParserDataStructureFactory _factory;
private PropertyListPath _parent;
private int _index;
private Object _object;
public PropertyListPath(Object object, ParserDataStructureFactory factory) {
this(null, 0, object, factory);
}
public PropertyListPath(PropertyListPath parent, int index, Object object, ParserDataStructureFactory factory) {
_parent = parent;
_index = index;
_object = object;
_factory = factory;
}
public int getIndex() {
return _index;
}
@Override
public int hashCode() {
return getKeyPath().hashCode();
}
@Override
public boolean equals(Object obj) {
return obj instanceof PropertyListPath && getKeyPath().equals(((PropertyListPath) obj).getKeyPath());
}
public int getIndexOf(PropertyListPath path) {
return getChildren().indexOf(path);
}
public PropertyListPath getChildAtIndex(int index) {
return getChildren().get(index);
}
public PropertyListPath getChildForKey(Object key) {
for (PropertyListPath child : getChildren()) {
if (key == child.getKey() || key.equals(child.getKey())) {
return child;
}
}
return null;
}
public List<PropertyListPath> getChildren() {
return getChildren(this, _object);
}
@SuppressWarnings( { "unchecked", "cast" })
protected List<PropertyListPath> getChildren(PropertyListPath path, Object pathObject) {
List<PropertyListPath> childrenList;
if (pathObject instanceof Map) {
Map lastPathMap = (Map) pathObject;
childrenList = new LinkedList<PropertyListPath>();
int index = 0;
List<Map.Entry<String, Object>> entries = new LinkedList<Map.Entry<String, Object>>((Set<Map.Entry<String, Object>>) lastPathMap.entrySet());
// Collections.sort(entries, new Comparator<Map.Entry<String, Object>>() {
// public int compare(Entry<String, Object> o1, Entry<String, Object> o2) {
// String k1 = o1.getKey();
// String k2 = o2.getKey();
// return k1.compareToIgnoreCase(k2);
// }
// });
for (Map.Entry<String, Object> entry : entries) {
PropertyListPath childPath = path.createChild(index++, entry);
childrenList.add(childPath);
}
} else if (pathObject instanceof Map.Entry) {
Map.Entry lastPathMapEntry = (Map.Entry) pathObject;
childrenList = getChildren(path, lastPathMapEntry.getValue());
} else if (pathObject instanceof List) {
List lastPathList = (List) pathObject;
childrenList = new LinkedList<PropertyListPath>();
int index = 0;
for (Object child : lastPathList) {
childrenList.add(path.createChild(index++, child));
}
} else if (pathObject instanceof Set) {
Set lastPathSet = (Set) pathObject;
childrenList = new LinkedList<PropertyListPath>();
int index = 0;
for (Object child : lastPathSet) {
childrenList.add(path.createChild(index++, child));
}
} else {
childrenList = new LinkedList<PropertyListPath>();
}
return childrenList;
}
public PropertyListPath getParent() {
return _parent;
}
public Object getRawObject() {
return _object;
}
public PropertyListPath createChild(int index, Object child) {
return new PropertyListPath(this, index, child, _factory);
}
public boolean isCollectionValue() {
Object object = _object;
if (object instanceof Map.Entry) {
object = ((Map.Entry) object).getValue();
}
boolean collectionValue;
if (object instanceof Map) {
collectionValue = true;
} else if (object instanceof Collection) {
collectionValue = true;
} else {
collectionValue = false;
}
return collectionValue;
}
public int getChildCount() {
int childCount;
Object value = getValue();
if (value instanceof Map) {
childCount = ((Map) value).size();
} else if (value instanceof Collection) {
childCount = ((Collection) value).size();
} else {
childCount = -1;
}
return childCount;
}
public String getKeyPath() {
StringBuffer sb = new StringBuffer();
getKeyPath(sb);
return sb.toString();
}
protected void getKeyPath(StringBuffer sb) {
if (_parent != null) {
_parent.getKeyPath(sb);
sb.append(".");
}
sb.append(getKey());
}
@SuppressWarnings("unchecked")
public boolean setKey(Object key) {
boolean valueChanged = false;
Object pathObject = getRawObject();
PropertyListPath parentPath = getParent();
if (parentPath == null) {
// IGNORE
} else if (pathObject instanceof Map.Entry) {
Object parentObject = parentPath.getRawObject();
Map parentMap;
if (parentObject instanceof Map) {
parentMap = (Map) parentObject;
} else {
parentMap = (Map) ((Map.Entry) parentObject).getValue();
}
String oldKey = getKey();
if (!parentMap.containsKey(key)) {
// MS: This bit of silliness is because we want to retain the order of the
// objects in the Map so that rows don't shuffle around as you tab through
// the editors making changes to keys. If we just remove and add the new
// key, the order will change, so instead we remove everything and re-add
// them in the original order, just replacing the one that we need to.
if (parentMap instanceof LinkedHashMap) {
List<Map.Entry> entries = new LinkedList<Map.Entry>(parentMap.entrySet());
parentMap.clear();
for (Map.Entry entry : entries) {
if (ComparisonUtils.equals(entry.getKey(), oldKey)) {
parentMap.put(key, entry.getValue());
} else {
parentMap.put(entry.getKey(), entry.getValue());
}
}
} else {
Object value = parentMap.remove(oldKey);
parentMap.put(key, value);
}
valueChanged = true;
}
}
return valueChanged;
}
public boolean isRealKey() {
Object pathObject = getRawObject();
PropertyListPath parentPath = getParent();
boolean isRealKey;
if (parentPath == null) {
isRealKey = false;
} else if (pathObject instanceof Map.Entry) {
isRealKey = true;
} else {
isRealKey = false;
}
return isRealKey;
}
public String getKey() {
Object pathObject = getRawObject();
PropertyListPath parentPath = getParent();
String key;
if (parentPath == null) {
key = "Root";
} else if (pathObject instanceof Map.Entry) {
key = String.valueOf(((Map.Entry) pathObject).getKey());
} else {
Object parentPathObject = parentPath.getValue();
if (parentPathObject instanceof List) {
key = "Item " + (getIndex() + 1);
} else {
key = "Unknown";
}
}
return key;
}
@SuppressWarnings("unchecked")
public boolean setValue(Object value) {
boolean parentChanged = false;
PropertyListPath parentPath = getParent();
if (parentPath == null) {
_object = value;
} else {
Object parentObject = parentPath.getValue();
if (parentObject instanceof Map) {
Map parentMap = (Map) parentObject;
String key = getKey();
parentMap.put(key, value);
} else if (parentObject instanceof List) {
int index = getIndex();
List parentList = (List) parentObject;
parentList.set(index, value);
parentChanged = true;
} else if (parentObject instanceof Set) {
Set parentSet = (Set) parentObject;
parentSet.remove(getValue());
parentSet.add(value);
parentChanged = true;
} else {
System.out.println("PropertyListPath.getValue: ignoring " + getKeyPath() + "=" + value);
}
}
return parentChanged;
}
public Object getValue() {
Object value = _object;
if (value instanceof Map.Entry) {
value = ((Map.Entry) value).getValue();
}
return value;
}
public PropertyListPath.Type getType() {
Object value = getValue();
for (PropertyListPath.Type type : PropertyListPath.Type.values()) {
if (type.matches(value)) {
return type;
}
}
return null;
}
public boolean setType(PropertyListPath.Type newType) {
Object newValue = convertValueToType(newType);
boolean parentChanged = setValue(newValue);
return parentChanged;
}
public Object convertValueToType(PropertyListPath.Type newType) {
Object newValue = PropertyListPath.convertValueFromTypeToType(getKeyPath(), getValue(), getType(), newType, _factory);
return newValue;
}
public ParserDataStructureFactory getFactory() {
return _factory;
}
@SuppressWarnings( { "unchecked", "cast" })
public static Object convertValueFromTypeToType(String keyPath, Object oldValue, PropertyListPath.Type oldType, PropertyListPath.Type newType, ParserDataStructureFactory factory) {
Object newValue;
try {
if (oldType == PropertyListPath.Type.Array) {
Object oldListValue = null;
List<Object> oldList = (List<Object>) oldValue;
if (oldList != null && oldList.size() == 1) {
oldListValue = oldList.get(0);
}
if (newType == PropertyListPath.Type.Array) {
newValue = oldValue;
} else if (newType == PropertyListPath.Type.Boolean) {
newValue = Boolean.TRUE;
if (oldListValue instanceof Boolean) {
newValue = (Boolean) oldListValue;
}
} else if (newType == PropertyListPath.Type.Data) {
if (oldListValue instanceof byte[]) {
newValue = (byte[]) oldListValue;
} else {
newValue = WOLPropertyListSerialization.stringFromPropertyList(oldValue).getBytes();
}
} else if (newType == PropertyListPath.Type.Date) {
if (oldListValue instanceof Date) {
newValue = (Date) oldListValue;
} else if (oldListValue instanceof Calendar) {
newValue = (Calendar) oldListValue;
} else {
newValue = new Date();
}
} else if (newType == PropertyListPath.Type.Dictionary) {
// REVIEW THIS ONE
Map<Object, Object> newMap = factory.createMap(keyPath);
int keyNum = 1;
for (Object obj : (Collection) oldValue) {
newMap.put("New Key " + (keyNum++), obj);
}
newValue = newMap;
} else if (newType == PropertyListPath.Type.Number) {
if (oldListValue instanceof Number) {
newValue = (Number) oldListValue;
} else if (oldValue != null) {
newValue = ((Collection) oldValue).size();
} else {
newValue = 0;
}
} else if (newType == PropertyListPath.Type.String) {
if (oldListValue instanceof String) {
newValue = (String) oldListValue;
} else {
newValue = WOLPropertyListSerialization.stringFromPropertyList(oldValue);
}
} else {
throw new IllegalArgumentException("Unknown type " + newType);
}
} else if (oldType == PropertyListPath.Type.Boolean) {
if (newType == PropertyListPath.Type.Array) {
Collection<Object> list = factory.createCollection(keyPath);
list.add(oldValue);
newValue = list;
} else if (newType == PropertyListPath.Type.Boolean) {
newValue = oldValue;
} else if (newType == PropertyListPath.Type.Data) {
newValue = new byte[] { 1 };
} else if (newType == PropertyListPath.Type.Date) {
// REVIEW THIS ONE
newValue = new Date();
} else if (newType == PropertyListPath.Type.Dictionary) {
Map<Object, Object> newMap = factory.createMap(keyPath);
newMap.put("New Key 1", oldValue);
newValue = newMap;
} else if (newType == PropertyListPath.Type.Number) {
newValue = ((Collection) oldValue).size();
} else if (newType == PropertyListPath.Type.String) {
newValue = WOLPropertyListSerialization.stringFromPropertyList(oldValue);
} else {
throw new IllegalArgumentException("Unknown type " + newType);
}
} else if (oldType == PropertyListPath.Type.Data) {
byte[] oldBytes = (byte[]) oldValue;
if (newType == PropertyListPath.Type.Array) {
Collection<Object> list = factory.createCollection(keyPath);
list.add(oldValue);
newValue = list;
} else if (newType == PropertyListPath.Type.Boolean) {
if (oldBytes != null && oldBytes.length == 1) {
newValue = oldBytes[0] > 0;
} else {
newValue = Boolean.TRUE;
}
} else if (newType == PropertyListPath.Type.Data) {
newValue = oldValue;
} else if (newType == PropertyListPath.Type.Date) {
// REVIEW THIS ONE
newValue = new Date();
} else if (newType == PropertyListPath.Type.Dictionary) {
Map<Object, Object> newMap = factory.createMap(keyPath);
newMap.put("New Key 1", oldValue);
newValue = newMap;
} else if (newType == PropertyListPath.Type.Number) {
if (oldBytes != null) {
String str = new String(oldBytes);
try {
newValue = new BigDecimal(str);
} catch (NumberFormatException e) {
newValue = oldBytes.length;
}
} else {
newValue = 0;
}
} else if (newType == PropertyListPath.Type.String) {
if (oldBytes != null) {
newValue = new String(oldBytes);
} else {
newValue = "";
}
} else {
throw new IllegalArgumentException("Unknown type " + newType);
}
} else if (oldType == PropertyListPath.Type.Date) {
if (newType == PropertyListPath.Type.Array) {
Collection<Object> list = factory.createCollection(keyPath);
list.add(oldValue);
newValue = list;
} else if (newType == PropertyListPath.Type.Boolean) {
newValue = Boolean.TRUE;
} else if (newType == PropertyListPath.Type.Data) {
// REVIEW THIS ONE
newValue = new byte[0];
} else if (newType == PropertyListPath.Type.Date) {
newValue = oldValue;
} else if (newType == PropertyListPath.Type.Dictionary) {
Map<Object, Object> newMap = factory.createMap(keyPath);
newMap.put("New Key 1", oldValue);
newValue = newMap;
} else if (newType == PropertyListPath.Type.Number) {
newValue = System.currentTimeMillis();
} else if (newType == PropertyListPath.Type.String) {
DateFormat dateFormat = DateFormat.getDateTimeInstance();
if (oldValue instanceof Date) {
newValue = dateFormat.format(oldValue);
} else if (oldValue instanceof Calendar) {
newValue = dateFormat.format(((Calendar) oldValue).getTime());
} else {
newValue = "";
}
} else {
throw new IllegalArgumentException("Unknown type " + newType);
}
} else if (oldType == PropertyListPath.Type.Dictionary) {
Map<Object, Object> oldMap = (Map<Object, Object>) oldValue;
Object oldMapValue = null;
if (oldMap != null && oldMap.size() == 1) {
Map.Entry<Object, Object> entry = oldMap.entrySet().iterator().next();
if ("New Key 1".equals(entry.getKey())) {
oldMapValue = entry.getValue();
}
}
if (newType == PropertyListPath.Type.Array) {
Collection<Object> list = factory.createCollection(keyPath);
for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) oldValue).entrySet()) {
list.add(entry.getValue());
}
newValue = list;
} else if (newType == PropertyListPath.Type.Boolean) {
if (oldMapValue instanceof Boolean) {
newValue = (Boolean) oldMapValue;
} else {
newValue = Boolean.TRUE;
}
} else if (newType == PropertyListPath.Type.Data) {
if (oldMapValue instanceof byte[]) {
newValue = (byte[]) oldMapValue;
} else {
newValue = WOLPropertyListSerialization.stringFromPropertyList(oldValue).getBytes();
}
} else if (newType == PropertyListPath.Type.Date) {
if (oldMapValue instanceof Date) {
newValue = (Date) oldMapValue;
} else if (oldMapValue instanceof Calendar) {
newValue = (Calendar) oldMapValue;
} else {
newValue = new Date();
}
} else if (newType == PropertyListPath.Type.Dictionary) {
newValue = oldValue;
} else if (newType == PropertyListPath.Type.Number) {
if (oldMapValue instanceof Number) {
newValue = (Number) oldMapValue;
} else if (oldMap != null) {
newValue = oldMap.size();
} else {
newValue = 0;
}
} else if (newType == PropertyListPath.Type.String) {
if (oldMapValue instanceof String) {
newValue = (String) oldMapValue;
} else {
newValue = WOLPropertyListSerialization.stringFromPropertyList(oldValue);
}
} else {
throw new IllegalArgumentException("Unknown type " + newType);
}
} else if (oldType == PropertyListPath.Type.Number) {
Number oldNumber = (Number) oldValue;
if (newType == PropertyListPath.Type.Array) {
Collection<Object> list = factory.createCollection(keyPath);
list.add(oldValue);
newValue = list;
} else if (newType == PropertyListPath.Type.Boolean) {
newValue = (oldNumber != null && oldNumber.intValue() > 0);
} else if (newType == PropertyListPath.Type.Data) {
newValue = String.valueOf(oldValue).getBytes();
} else if (newType == PropertyListPath.Type.Date) {
newValue = new Date(System.currentTimeMillis());
} else if (newType == PropertyListPath.Type.Dictionary) {
Map<Object, Object> newMap = factory.createMap(keyPath);
newMap.put("New Key 1", oldValue);
newValue = newMap;
} else if (newType == PropertyListPath.Type.Number) {
newValue = oldValue;
} else if (newType == PropertyListPath.Type.String) {
newValue = String.valueOf(oldNumber);
} else {
throw new IllegalArgumentException("Unknown type " + newType);
}
} else if (oldType == PropertyListPath.Type.String) {
String oldString = (String) oldValue;
if (newType == PropertyListPath.Type.Array) {
Collection<Object> list = factory.createCollection(keyPath);
list.add(oldValue);
newValue = list;
} else if (newType == PropertyListPath.Type.Boolean) {
if (oldString == null) {
newValue = Boolean.FALSE;
} else {
Object newObject;
try {
newObject = "y".equalsIgnoreCase(oldString) || "yes".equalsIgnoreCase(oldString) || "true".equalsIgnoreCase(oldString);
newValue = (newObject instanceof Boolean) ? newObject : Boolean.TRUE;
} catch (Exception e) {
newValue = Boolean.FALSE;
}
}
} else if (newType == PropertyListPath.Type.Data) {
newValue = WOLPropertyListSerialization.stringFromPropertyList(oldValue).getBytes();
} else if (newType == PropertyListPath.Type.Date) {
DateFormat dateFormat = DateFormat.getDateTimeInstance();
if (oldValue == null) {
newValue = new Date();
} else {
try {
newValue = dateFormat.parseObject((String) oldValue);
} catch (ParseException e) {
newValue = new Date();
}
}
} else if (newType == PropertyListPath.Type.Dictionary) {
Map<Object, Object> newMap = factory.createMap(keyPath);
newMap.put("New Key 1", oldValue);
newValue = newMap;
} else if (newType == PropertyListPath.Type.Number) {
if (oldValue == null) {
newValue = 0;
} else {
try {
newValue = new BigDecimal(oldString);
} catch (NumberFormatException e) {
newValue = 0;
}
}
} else if (newType == PropertyListPath.Type.String) {
newValue = oldValue;
} else {
throw new IllegalArgumentException("Unknown type " + newType);
}
} else {
throw new IllegalArgumentException("Unknown old type " + oldType);
}
} catch (PropertyListParserException e) {
throw new IllegalArgumentException("Failed to process plist.", e);
}
return newValue;
}
@SuppressWarnings("unchecked")
public PropertyListPath addRow() {
PropertyListPath newPath;
Object value = getValue();
if (value instanceof Map) {
Map<Object, Object> map = (Map<Object, Object>) value;
int i = 1;
String key;
do {
key = "New Key " + (i++);
} while (map.containsKey(key));
map.put(key, "New Value");
newPath = getChildForKey(key);
} else if (value instanceof List) {
List<Object> list = (List<Object>) value;
list.add("New Value");
newPath = getChildAtIndex(list.size() - 1);
} else if (value instanceof Set) {
Set<Object> set = (Set<Object>) value;
int i = 1;
String key;
do {
key = "New Value " + (i++);
} while (set.contains(key));
newPath = null;
for (PropertyListPath child : getChildren()) {
if (key.equals(child.getValue())) {
newPath = child;
break;
}
}
} else {
newPath = null;
}
return newPath;
}
public boolean delete() {
boolean deleted = false;
PropertyListPath parentPath = getParent();
if (parentPath != null) {
Object parentValue = parentPath.getValue();
if (parentValue instanceof List) {
((List) parentValue).remove(getIndex());
deleted = true;
} else if (parentValue instanceof Set) {
((Set) parentValue).remove(getValue());
deleted = true;
} else if (parentValue instanceof Map) {
((Map) parentValue).remove(getKey());
deleted = true;
}
}
return deleted;
}
@SuppressWarnings("unchecked")
public boolean moveUp() {
boolean moved = false;
PropertyListPath parentPath = getParent();
if (parentPath != null) {
Object parentValue = parentPath.getValue();
if (parentValue instanceof List) {
int index = getIndex();
if (index > 0) {
Object obj = ((List) parentValue).remove(index);
((List) parentValue).add(index - 1, obj);
moved = true;
}
} else if (parentValue instanceof Set) {
//((Set) parentValue).remove(getValue());
//moved = true;
} else if (parentValue instanceof Map) {
//((Map) parentValue).remove(getKey());
//moved = true;
}
}
return moved;
}
@SuppressWarnings("unchecked")
public boolean moveDown() {
boolean moved = false;
PropertyListPath parentPath = getParent();
if (parentPath != null) {
Object parentValue = parentPath.getValue();
if (parentValue instanceof List) {
int index = getIndex();
if (index < parentPath.getChildCount() - 1) {
Object obj = ((List) parentValue).remove(index);
((List) parentValue).add(index + 1, obj);
moved = true;
}
} else if (parentValue instanceof Set) {
//((Set) parentValue).remove(getValue());
//moved = true;
} else if (parentValue instanceof Map) {
//((Map) parentValue).remove(getKey());
//moved = true;
}
}
return moved;
}
}