| /* |
| * 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); |
| } |
| } |
| |
| } |