blob: c199f6b4196323734490f3a67dbe261928708d99 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.persistence.proxy;
import java.io.PrintStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Version;
import org.apache.openjpa.persistence.DetachedState;
import org.apache.openjpa.persistence.ElementDependent;
import org.apache.openjpa.persistence.jdbc.ElementJoinColumn;
import org.apache.openjpa.persistence.jdbc.OrderColumn;
/**
* Persistent entity for testing adding/removing elements of collection valued
* field while in detached state.
*
* Node refers to a list of Nodes as children.
*
* Contains recursive methods to create or modify uniform subtree. Uniform
* subtree implies that each child at a level L has equal number of
* grand children at level L+1.
*
* @author Pinaki Poddar
*
*/
@Entity
@DetachedState
public class TreeNode implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
private long id;
private String name;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@ElementJoinColumn(name = "ParentID")
@OrderColumn(name = "Sequence")
@ElementDependent
private List<TreeNode> childern = new ArrayList<>();
@Version
private int version;
public long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* Add a child node at the end of the current list of children.
*/
public void addNode(TreeNode node) {
addNode(node, childern.size());
}
/**
* Insert a child node at the specified position in the list of children.
*/
public void addNode(TreeNode node, int position) {
checkSequenceRange(position);
childern.add(position, node);
}
public boolean removeNode(TreeNode node) {
return childern.remove(node);
}
public TreeNode removeNode(int sequence) {
checkSequenceRange(sequence);
return childern.remove(sequence);
}
public TreeNode getNode(int sequence) {
checkSequenceRange(sequence);
return childern.get(sequence);
}
public List<TreeNode> getNodes() {
return childern;
}
public void clearNodes() {
childern.clear();
}
public boolean isLeaf() {
return childern.isEmpty();
}
protected void checkSequenceRange(int sequence)
throws IllegalArgumentException {
int size = childern.size();
if (sequence < 0 || sequence > size)
throw new IllegalArgumentException("Sequence number is beyond "
+ "range of 0 to " + size + ".");
}
public int getVersion() {
return version;
}
/**
* Create a uniform subtree below the receiver. Uniform subtree implies that
* each child at a level L has equal number of grand children at level L+1.
*
* @param fanOuts
* array of fan outs for children at every level.
*/
public void createTree(int[] fanOuts) {
if (fanOuts.length == 0)
return;
int[] nextFanOuts = new int[fanOuts.length];
System.arraycopy(fanOuts, 1, nextFanOuts, 0, fanOuts.length - 1);
for (int j = 0; j < fanOuts[0]; j++) {
TreeNode child = new TreeNode();
child.setName(getName() + "." + j);
addNode(child);
child.createTree(nextFanOuts);
}
}
/**
* Add or remove subtree of the receiver to match the given fanOut.
*/
public void modify(int[] fanOuts) {
if (fanOuts == null || fanOuts.length == 0)
return;
int n = fanOuts[0];
int[] nextFanOuts = new int[fanOuts.length];
System.arraycopy(fanOuts, 1, nextFanOuts, 0, fanOuts.length - 1);
List<TreeNode> children = getNodes();
int diff = children.size() - n;
if (diff < 0) {
for (int i = 0; i < -diff; i++) {
TreeNode newChild = new TreeNode();
int position = getNodes().size();
newChild.setName(getName() + "." + position);
addNode(newChild);
}
} else if (diff > 0) {
for (int i = 0; i < diff; i++) {
int position = getNodes().size() - 1;
removeNode(position);
}
}
children = getNodes();
for (TreeNode child : children) {
child.modify(nextFanOuts);
}
}
/**
* Get the fan outs of the given receiver. Assumes that the subtree is
* uniform. Otherwise throws exception.
*/
public int[] getFanOuts() {
return getFanOuts(new int[] {});
}
private int[] getFanOuts(int[] list) {
List<TreeNode> children = getNodes();
if (children.isEmpty())
return list;
int[] fanOuts = new int[children.size()];
int i = 0;
for (TreeNode child : children) {
fanOuts[i++] = child.getNodes().size();
}
for (int j = 0; j < fanOuts.length - 1; j++)
if (fanOuts[j] != fanOuts[j + 1])
throw new RuntimeException("non-uniform fanouts for children "
+ " of " + getName());
int[] newList = new int[list.length + 1];
System.arraycopy(list, 0, newList, 0, list.length);
newList[list.length] = children.size();
return children.get(0).getFanOuts(newList);
}
/**
* Prints this receiver and its subtree.
*/
public void print(PrintStream out) {
print(2, out);
}
private void print(int tab, PrintStream out) {
for (int i = 0; i < tab; i++)
out.print(" ");
out.println(getName());
for (TreeNode child : getNodes()) {
child.print(tab + 2, out);
}
}
}