/**
* Copyright (c) 2011, SOCIETIES Consortium (WATERFORD INSTITUTE OF TECHNOLOGY (TSSG), HERIOT-WATT UNIVERSITY (HWU), SOLUTA.NET
* (SN), GERMAN AEROSPACE CENTRE (Deutsches Zentrum fuer Luft- und Raumfahrt e.V.) (DLR), Zavod za varnostne tehnologije
* informacijske družbe in elektronsko poslovanje (SETCCE), INSTITUTE OF COMMUNICATION AND COMPUTER SYSTEMS (ICCS), LAKE
* COMMUNICATIONS (LAKE), INTEL PERFORMANCE LEARNING SOLUTIONS LTD (INTEL), PORTUGAL TELECOM INOVAÇÃO, SA (PTIN), IBM Corp.,
* INSTITUT TELECOM (ITSUD), AMITEC DIACHYTI EFYIA PLIROFORIKI KAI EPIKINONIES ETERIA PERIORISMENIS EFTHINIS (AMITEC), TELECOM
* ITALIA S.p.a.(TI), TRIALOG (TRIALOG), Stiftelsen SINTEF (SINTEF), NEC EUROPE LTD (NEC))
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.societies.android.api.internal.personalisation.model;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.Stack;
import java.util.Vector;
import android.os.Parcel;
import android.os.Parcelable;
/**
* @author Elizabeth
* @version 1.0
* @created 08-Nov-2011 14:02:57
*/
public class APreference implements Parcelable {
/**
* An enumeration that is always empty. This is used when an enumeration
* of a leaf node's children is requested.
*/
static public final Enumeration<APreference> EMPTY_ENUMERATION
= new Enumeration<APreference>() {
public boolean hasMoreElements() { return false; }
public APreference nextElement() {
throw new NoSuchElementException("No more elements");
}
};
/** this node's parent, or null if this node has no parent */
protected APreference parent;
/** array of children, may be null if this node has no children */
protected Vector<APreference> children;
/** optional user object */
transient protected Object userObject;
/** true if the node is able to have children */
protected boolean allowsChildren;
public APreference(){
this(null, true);
}
public APreference(AContextPreferenceCondition pc){
this(pc, true);
}
public APreference(APreferenceOutcome po){
this(po,false);
}
private APreference(Object obj, boolean allowsCh){
parent = null;
this.allowsChildren = allowsCh;
this.userObject = obj;
}
public APreferenceOutcome getOutcome() {
Object obj = this.userObject;
if (obj instanceof APreferenceOutcome){
return (APreferenceOutcome) obj;
}
return null;
}
public void writeToParcel(Parcel out, int flags) {
out.writeByte((byte) (allowsChildren ? 1 : 0));
Parcelable[] aChildren = new Parcelable[this.children.size()];
for (int i =0; i< this.children.size(); i++){
aChildren[i] = (Parcelable) this.children.get(i);
}
out.writeParcelableArray(aChildren, flags);
out.writeParcelable((Parcelable) parent, flags);
out.writeParcelable((Parcelable) userObject, flags);
}
public APreference(Parcel in){
super();
this.allowsChildren = in.readByte() == 1;
this.readChildren((APreference[]) in.readParcelableArray(APreference.class.getClassLoader()));
this.parent = (APreference) in.readParcelable(APreference.class.getClassLoader());
if (this.allowsChildren){
try{
this.userObject = in.readParcelable(AContextPreferenceCondition.class.getClassLoader());
} catch(Exception e){
e.printStackTrace();
}
}else{
this.userObject = in.readParcelable(APreferenceOutcome.class.getClassLoader());
}
}
private void readChildren(APreference[] readParcelableArray) {
for (APreference pr : readParcelableArray){
this.children.add((APreference) pr);
}
}
public static final Parcelable.Creator<APreference> CREATOR = new Parcelable.Creator<APreference>() {
public APreference createFromParcel(Parcel in) {
return new APreference(in);
}
public APreference[] newArray(int size) {
return new APreference[size];
}
};
public AContextPreferenceCondition getCondition() {
if (this.userObject instanceof AContextPreferenceCondition){
return (AContextPreferenceCondition) this.userObject;
}
return null;
}
public boolean isBranch() {
if (this.userObject==null){
return true;
}
if (this.userObject instanceof AContextPreferenceCondition){
return true;
}
return false;
}
public boolean isLeaf(){
if (this.userObject instanceof AContextPreferenceCondition){
return false;
}
if (this.userObject==null){
return false;
}
return true;
}
public Object[] getUserObjectPath(){
APreference[] realPath = getPath();
Object[] retPath = new Object[realPath.length];
for(int counter = 0; counter < realPath.length; counter++)
retPath[counter] = ((APreference)realPath[counter])
.getUserObject();
return retPath;
}
public Object getUserObject(){
return this.userObject;
}
public APreference[] getPath() {
return getPathToRoot(this, 0);
}
protected APreference[] getPathToRoot(APreference aNode, int depth) {
APreference[] retNodes;
/* Check for null, in case someone passed in a null node, or
they passed in an element that isn't rooted at root. */
if(aNode == null) {
if(depth == 0)
return null;
else
retNodes = new APreference[depth];
}
else {
depth++;
retNodes = getPathToRoot(aNode.getParent(), depth);
retNodes[retNodes.length - depth] = aNode;
}
return retNodes;
}
public void add(APreference newChild) {
if(newChild != null && newChild.getParent() == this)
insert(newChild, getChildCount() - 1);
else
insert(newChild, getChildCount());
}
public void insert(APreference newChild, int childIndex) {
if (!allowsChildren) {
throw new IllegalStateException("node does not allow children");
} else if (newChild == null) {
throw new IllegalArgumentException("new child is null");
} else if (isNodeAncestor(newChild)) {
throw new IllegalArgumentException("new child is an ancestor");
}
APreference oldParent = (APreference)newChild.getParent();
if (oldParent != null) {
oldParent.remove(newChild);
}
newChild.setParent(this);
if (children == null) {
children = new Vector<APreference>();
}
children.insertElementAt(newChild, childIndex);
}
public void setParent(APreference newParent) {
parent = newParent;
}
public boolean isNodeAncestor(APreference anotherNode) {
if (anotherNode == null) {
return false;
}
APreference ancestor = this;
do {
if (ancestor == anotherNode) {
return true;
}
} while((ancestor = ancestor.getParent()) != null);
return false;
}
/**
* Returns the number of children of this node.
*
* @return an int giving the number of children of this node
*/
public int getChildCount() {
if (children == null) {
return 0;
} else {
return children.size();
}
}
public void remove(APreference aChild) {
if (aChild == null) {
throw new IllegalArgumentException("argument is null");
}
if (!isNodeChild(aChild)) {
throw new IllegalArgumentException("argument is not a child");
}
remove(getIndex(aChild)); // linear search
}
public void remove(int childIndex) {
APreference child = (APreference)getChildAt(childIndex);
children.removeElementAt(childIndex);
child.setParent(null);
}
public APreference getChildAt(int index) {
if (children == null) {
throw new ArrayIndexOutOfBoundsException("node has no children");
}
return (APreference)children.elementAt(index);
}
public int getIndex(APreference aChild) {
if (aChild == null) {
throw new IllegalArgumentException("argument is null");
}
if (!isNodeChild(aChild)) {
return -1;
}
return children.indexOf(aChild); // linear search
}
public boolean isNodeChild(APreference aNode) {
boolean retval;
if (aNode == null) {
retval = false;
} else {
if (getChildCount() == 0) {
retval = false;
} else {
retval = (aNode.getParent() == this);
}
}
return retval;
}
public Enumeration<APreference> depthFirstEnumeration() {
return postorderEnumeration();
}
public Enumeration<APreference> breadthFirstEnumeration() {
return new BreadthFirstEnumeration(this);
}
public APreference getRoot() {
APreference ancestor = this;
APreference previous;
do {
previous = ancestor;
ancestor = ancestor.getParent();
} while (ancestor != null);
return previous;
}
public int getLevel() {
APreference ancestor;
int levels = 0;
ancestor = this;
while((ancestor = ancestor.getParent()) != null){
levels++;
}
return levels;
}
public int getDepth() {
Object last = null;
Enumeration<APreference> enum_ = breadthFirstEnumeration();
while (enum_.hasMoreElements()) {
last = enum_.nextElement();
}
if (last == null) {
throw new Error ("nodes should be null");
}
return ((APreference)last).getLevel() - getLevel();
}
public Enumeration<APreference> children() {
if (children == null) {
return EMPTY_ENUMERATION;
} else {
return children.elements();
}
}
/**
* @see DefaultMutableTreeNode#postorderEnumeration()
* @return an enumeration of IPreference node objects traversed in post-order
*/
public Enumeration<APreference> postorderEnumeration() {
return new PostorderEnumeration(this);
}
public Enumeration preorderEnumeration() {
return new PreorderEnumeration(this);
}
public APreference getParent(){
return (APreference) parent;
}
/*
public String toString(){
String str = "";
if (this.isLeaf()){
String tab = "\n";
for (int i = 0; i<this.getLevel(); i++){
tab = tab.concat("\t");
}
return tab.concat(this.getOutcome().toString());
}else{
String tab = "\n";
if (null!=this.userObject){
for (int i=0; i<this.getLevel(); i++){
str = str.concat("\t");
}
str = str + this.userObject.toString()+"\n";
}
Enumeration<IPreference> e = this.children();
while (e.hasMoreElements()){
str = str+e.nextElement().toString()+"\n";
}
return str;
}
}*/
public String toString(){
if (this.userObject==null){
return "root";
}
return this.getUserObject().toString();
}
public String toTreeString(){
String str = "";
Enumeration<APreference> e = this.preorderEnumeration();
while (e.hasMoreElements()){
APreference p = e.nextElement();
for (int i = 0; i<p.getLevel(); i++){
str = str.concat("\t");
}
str = str.concat(p.toString()+"\n");
}
return str;
}
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
final class BreadthFirstEnumeration implements Enumeration<APreference> {
protected Queue queue;
public BreadthFirstEnumeration(APreference rootNode) {
super();
Vector<APreference> v = new Vector<APreference>(1);
v.addElement(rootNode); // PENDING: don't really need a vector
queue = new Queue();
queue.enqueue(v.elements());
}
public boolean hasMoreElements() {
return (!queue.isEmpty() &&
((Enumeration)queue.firstObject()).hasMoreElements());
}
public APreference nextElement() {
Enumeration enumer = (Enumeration)queue.firstObject();
APreference node = (APreference)enumer.nextElement();
Enumeration<APreference> children = node.children();
if (!enumer.hasMoreElements()) {
queue.dequeue();
}
if (children.hasMoreElements()) {
queue.enqueue(children);
}
return node;
}
// A simple queue with a linked list data structure.
final class Queue {
QNode head; // null if empty
QNode tail;
final class QNode {
public Object object;
public QNode next; // null if end
public QNode(Object object, QNode next) {
this.object = object;
this.next = next;
}
}
public void enqueue(Object anObject) {
if (head == null) {
head = tail = new QNode(anObject, null);
} else {
tail.next = new QNode(anObject, null);
tail = tail.next;
}
}
public Object dequeue() {
if (head == null) {
throw new NoSuchElementException("No more elements");
}
Object retval = head.object;
QNode oldHead = head;
head = head.next;
if (head == null) {
tail = null;
} else {
oldHead.next = null;
}
return retval;
}
public Object firstObject() {
if (head == null) {
throw new NoSuchElementException("No more elements");
}
return head.object;
}
public boolean isEmpty() {
return head == null;
}
} // End of class Queue
} // End of class BreadthFirstEnumeration
final class PreorderEnumeration implements Enumeration<APreference> {
protected Stack stack;
public PreorderEnumeration(APreference rootNode) {
super();
Vector<APreference> v = new Vector<APreference>(1);
v.addElement(rootNode); // PENDING: don't really need a vector
stack = new Stack();
stack.push(v.elements());
}
public boolean hasMoreElements() {
return (!stack.empty() &&
((Enumeration)stack.peek()).hasMoreElements());
}
public APreference nextElement() {
Enumeration enumer = (Enumeration)stack.peek();
APreference node = (APreference)enumer.nextElement();
Enumeration children = node.children();
if (!enumer.hasMoreElements()) {
stack.pop();
}
if (children.hasMoreElements()) {
stack.push(children);
}
return node;
}
} // End of class PreorderEnumeration
final class PostorderEnumeration implements Enumeration<APreference> {
protected APreference root;
protected Enumeration<APreference> children;
protected Enumeration<APreference> subtree;
public PostorderEnumeration(APreference rootNode) {
super();
root = rootNode;
children = root.children();
subtree = EMPTY_ENUMERATION;
}
public boolean hasMoreElements() {
return root != null;
}
public APreference nextElement() {
APreference retval;
if (subtree.hasMoreElements()) {
retval = subtree.nextElement();
} else if (children.hasMoreElements()) {
subtree = new PostorderEnumeration(
(APreference)children.nextElement());
retval = subtree.nextElement();
} else {
retval = root;
root = null;
}
return retval;
}
} // End of class PostorderEnumeration
}