/*
* Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package jace.state;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This represents an object graph in a serializable way. The emulator object
* tree is not preserved in a saved state (it would be too bulky) so it is
* important that state can be restored. This means that serialized object
* values need to be merged to a new object graph, and that graph needs to be
* traversed cleanly and correctly.
*
* @author Brendan Robert (BLuRry) brendan.robert@gmail.com
* @param <T>
*/
public class ObjectGraphNode<T> implements Serializable {
public ObjectGraphNode parent;
public String name;
public int index;
public boolean isStateful;
public boolean forceCheck; // If true, object must always be inspected for changes -- more expensive
public List<ObjectGraphNode> children;
transient public WeakReference<T> source;
transient public DirtyFlag dirty;
transient public Class<T> type;
public boolean isPrimitive = false;
public static enum DirtyFlag {
UNKNOWN, CLEAN, DIRTY
};
public ObjectGraphNode(Class<T> clazz) {
children = new ArrayList<>();
type = clazz;
dirty = DirtyFlag.UNKNOWN;
forceCheck = true;
}
public ObjectGraphNode(T obj) {
this((Class<T>) obj.getClass());
source = (WeakReference<T>) new WeakReference(obj);
}
public T getCurrentValue() {
if (isPrimitive || type.isPrimitive()) {
try {
return (T) parent.type.getField(name).get(parent.getCurrentValue());
} catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException ex) {
Logger.getLogger(ObjectGraphNode.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
} else {
return source.get();
}
}
public void markClean() {
dirty = DirtyFlag.CLEAN;
}
public void markDirty() {
dirty = DirtyFlag.DIRTY;
}
public boolean isDirty() {
return dirty == DirtyFlag.DIRTY;
}
public void setCurrentValue(Object value) {
if (parent == null) {
return;
}
if (List.class.isAssignableFrom(parent.type)) {
// This is a list index
List p = (List) parent.getCurrentValue();
p.set(index, value);
} else if (Map.class.isAssignableFrom(parent.type)) {
// this is a map entry
Map p = (Map) parent.getCurrentValue();
// TODO: If keys are not strings then this will not work -- write something to resolve keys
p.put(name, value);
} else if (parent.type.isArray()) {
// This is an array index
Object[] p = (Object[]) parent.getCurrentValue();
if (p != null) {
p[index] = value;
}
} else {
// Must be an object member
Object p = parent.getCurrentValue();
try {
parent.type.getField(name).set(p, value);
} catch (NoSuchFieldException ex) {
System.out.println(parent.name + "." + name);
System.out.println("This = "+type);
System.out.println("Parent = " + parent.type);
System.out.println("this Is Array: " + type.isArray());
System.out.println("parent Is Array: " + parent.type.isArray());
Logger.getLogger(ObjectGraphNode.class.getName()).log(Level.SEVERE, null, ex);
} catch (SecurityException | IllegalArgumentException | IllegalAccessException ex) {
Logger.getLogger(ObjectGraphNode.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public ObjectGraphNode find(String path) {
String[] parts = path.split("\\.");
ObjectGraphNode current = this;
for (String part : parts) {
for (ObjectGraphNode child : (List<ObjectGraphNode>) current.children) {
if (child.name.equals(part)) {
current = child;
break;
} else {
return null;
}
}
}
return current;
}
public boolean valueChanged(State tail) {
if (!forceCheck || isDirty()) {
return isDirty();
}
//?? Let's be lazy and just say yes for now.
return true;
}
@Override
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof ObjectGraphNode)) {
return false;
}
ObjectGraphNode node = (ObjectGraphNode) obj;
if (parent == null) {
if (node.parent != null) {
return false;
}
} else {
if (node.parent == null) {
return false;
} else if (!node.parent.equals(parent)) {
return false;
}
}
return (name.equals(node.name) && index == node.index);
}
}