001 /* 002 * JBoss DNA (http://www.jboss.org/dna) 003 * See the COPYRIGHT.txt file distributed with this work for information 004 * regarding copyright ownership. Some portions may be licensed 005 * to Red Hat, Inc. under one or more contributor license agreements. 006 * See the AUTHORS.txt file in the distribution for a full listing of 007 * individual contributors. 008 * 009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA 010 * is licensed to you under the terms of the GNU Lesser General Public License as 011 * published by the Free Software Foundation; either version 2.1 of 012 * the License, or (at your option) any later version. 013 * 014 * JBoss DNA is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 017 * Lesser General Public License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this software; if not, write to the Free 021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 023 */ 024 package org.jboss.dna.connector.store.jpa.model.basic; 025 026 import java.util.List; 027 import javax.persistence.Column; 028 import javax.persistence.Entity; 029 import javax.persistence.EntityManager; 030 import javax.persistence.Id; 031 import javax.persistence.JoinColumn; 032 import javax.persistence.ManyToOne; 033 import javax.persistence.NamedQueries; 034 import javax.persistence.NamedQuery; 035 import javax.persistence.Query; 036 import javax.persistence.Table; 037 import org.hibernate.annotations.Index; 038 import org.jboss.dna.common.text.Inflector; 039 import org.jboss.dna.common.util.HashCode; 040 import org.jboss.dna.connector.store.jpa.model.common.NamespaceEntity; 041 042 /** 043 * An entity representing the parent-child relationship between two nodes. In addition to the references to the parent and child 044 * nodes, this entity also maintains the indexInParent of the indexInParent within the parent node's list of all children, the 045 * child's name ( {@link #getChildName() local part} and {@link #getChildNamespace() namespace}), and the same-name-sibiling 046 * indexInParent (if there is one). 047 * 048 * @author Randall Hauch 049 */ 050 @Entity 051 @Table( name = "DNA_BASIC_CHILDREN" ) 052 @org.hibernate.annotations.Table( appliesTo = "DNA_BASIC_CHILDREN", indexes = { 053 @Index( name = "CHILDINDEX_INX", columnNames = {"WORKSPACE_ID", "PARENT_UUID", "CHILD_INDEX"} ), 054 @Index( name = "CHILDUUID_INX", columnNames = {"WORKSPACE_ID", "CHILD_UUID"} ), 055 @Index( name = "CHILDNAME_INX", columnNames = {"WORKSPACE_ID", "PARENT_UUID", "CHILD_NAME_NS_ID", "CHILD_NAME_LOCAL", 056 "SNS_INDEX"} )} ) 057 @NamedQueries( { 058 @NamedQuery( name = "ChildEntity.findByPathSegment", query = "select child from ChildEntity as child where child.id.workspaceId = :workspaceId and child.id.parentUuidString = :parentUuidString AND child.childNamespace.id = :ns AND child.childName = :childName AND child.sameNameSiblingIndex = :sns" ), 059 @NamedQuery( name = "ChildEntity.findAllUnderParent", query = "select child from ChildEntity as child where child.id.workspaceId = :workspaceId and child.id.parentUuidString = :parentUuidString order by child.indexInParent" ), 060 @NamedQuery( name = "ChildEntity.findRangeUnderParent", query = "select child from ChildEntity as child where child.id.workspaceId = :workspaceId and child.id.parentUuidString = :parentUuidString and child.indexInParent >= :firstIndex and child.indexInParent < :afterIndex order by child.indexInParent" ), 061 @NamedQuery( name = "ChildEntity.findChildrenAfterIndexUnderParent", query = "select child from ChildEntity as child where child.id.workspaceId = :workspaceId and child.id.parentUuidString = :parentUuidString and child.indexInParent >= :afterIndex order by child.indexInParent" ), 062 @NamedQuery( name = "ChildEntity.findByChildUuid", query = "select child from ChildEntity as child where child.id.workspaceId = :workspaceId and child.id.childUuidString = :childUuidString" ), 063 @NamedQuery( name = "ChildEntity.findMaximumSnsIndex", query = "select max(child.sameNameSiblingIndex) from ChildEntity as child where child.id.workspaceId = :workspaceId and child.id.parentUuidString = :parentUuid AND child.childNamespace.id = :ns AND child.childName = :childName" ), 064 @NamedQuery( name = "ChildEntity.findMaximumChildIndex", query = "select max(child.indexInParent) from ChildEntity as child where child.id.workspaceId = :workspaceId and child.id.parentUuidString = :parentUuid" ), 065 @NamedQuery( name = "ChildEntity.findInWorkspace", query = "select child from ChildEntity as child where child.id.workspaceId = :workspaceId" )} ) 066 public class ChildEntity { 067 068 @Id 069 private ChildId id; 070 071 /** The zero-based index */ 072 @Column( name = "CHILD_INDEX", nullable = false, unique = false ) 073 private int indexInParent; 074 075 @ManyToOne 076 @JoinColumn( name = "CHILD_NAME_NS_ID", nullable = false ) 077 private NamespaceEntity childNamespace; 078 079 @Column( name = "CHILD_NAME_LOCAL", nullable = false, unique = false, length = 512 ) 080 private String childName; 081 082 @Column( name = "SNS_INDEX", nullable = false, unique = false ) 083 private int sameNameSiblingIndex; 084 085 /** 086 * Tracks whether this node allows or disallows its children to have the same names (to be same-name-siblings). The model uses 087 * this to know whether it can optimization the database operations when creating, inserting, or removing children. 088 */ 089 @Column( name = "ALLOWS_SNS", nullable = false, unique = false ) 090 private boolean allowsSameNameChildren; 091 092 public ChildEntity() { 093 } 094 095 public ChildEntity( ChildId id, 096 int indexInParent, 097 NamespaceEntity ns, 098 String name ) { 099 this.id = id; 100 this.indexInParent = indexInParent; 101 this.childNamespace = ns; 102 this.childName = name; 103 this.sameNameSiblingIndex = 1; 104 } 105 106 public ChildEntity( ChildId id, 107 int indexInParent, 108 NamespaceEntity ns, 109 String name, 110 int sameNameSiblingIndex ) { 111 this.id = id; 112 this.indexInParent = indexInParent; 113 this.childNamespace = ns; 114 this.childName = name; 115 this.sameNameSiblingIndex = sameNameSiblingIndex; 116 } 117 118 /** 119 * @return parent 120 */ 121 public ChildId getId() { 122 return id; 123 } 124 125 /** 126 * @param childId Sets parent to the specified value. 127 */ 128 public void setId( ChildId childId ) { 129 this.id = childId; 130 } 131 132 /** 133 * Get the zero-based index of this child within the parent's list of children 134 * 135 * @return the zero-based index of this child 136 */ 137 public int getIndexInParent() { 138 return indexInParent; 139 } 140 141 /** 142 * @param index Sets indexInParent to the specified value. 143 */ 144 public void setIndexInParent( int index ) { 145 this.indexInParent = index; 146 } 147 148 /** 149 * @return childName 150 */ 151 public String getChildName() { 152 return childName; 153 } 154 155 /** 156 * @param childName Sets childName to the specified value. 157 */ 158 public void setChildName( String childName ) { 159 this.childName = childName; 160 } 161 162 /** 163 * @return childNamespace 164 */ 165 public NamespaceEntity getChildNamespace() { 166 return childNamespace; 167 } 168 169 /** 170 * @param childNamespace Sets childNamespace to the specified value. 171 */ 172 public void setChildNamespace( NamespaceEntity childNamespace ) { 173 this.childNamespace = childNamespace; 174 } 175 176 /** 177 * @return sameNameSiblingIndex 178 */ 179 public int getSameNameSiblingIndex() { 180 return sameNameSiblingIndex; 181 } 182 183 /** 184 * @param sameNameSiblingIndex Sets sameNameSiblingIndex to the specified value. 185 */ 186 public void setSameNameSiblingIndex( int sameNameSiblingIndex ) { 187 this.sameNameSiblingIndex = sameNameSiblingIndex; 188 } 189 190 /** 191 * @return allowsSameNameChildren 192 */ 193 public boolean getAllowsSameNameChildren() { 194 return allowsSameNameChildren; 195 } 196 197 /** 198 * @param allowsSameNameChildren Sets allowsSameNameChildren to the specified value. 199 */ 200 public void setAllowsSameNameChildren( boolean allowsSameNameChildren ) { 201 this.allowsSameNameChildren = allowsSameNameChildren; 202 } 203 204 /** 205 * {@inheritDoc} 206 * 207 * @see java.lang.Object#hashCode() 208 */ 209 @Override 210 public int hashCode() { 211 return HashCode.compute(id); 212 } 213 214 /** 215 * {@inheritDoc} 216 * 217 * @see java.lang.Object#equals(java.lang.Object) 218 */ 219 @Override 220 public boolean equals( Object obj ) { 221 if (obj == this) return true; 222 if (obj instanceof ChildEntity) { 223 ChildEntity that = (ChildEntity)obj; 224 if (this.id == null) { 225 if (that.id != null) return false; 226 } else { 227 if (!this.id.equals(that.id)) return false; 228 } 229 return true; 230 } 231 return false; 232 } 233 234 /** 235 * {@inheritDoc} 236 * 237 * @see java.lang.Object#toString() 238 */ 239 @Override 240 public String toString() { 241 StringBuilder sb = new StringBuilder(); 242 if (childNamespace != null) { 243 sb.append('{').append(childNamespace).append("}:"); 244 } 245 sb.append(childName); 246 if (sameNameSiblingIndex > 1) { 247 sb.append('[').append(sameNameSiblingIndex).append(']'); 248 } 249 if (id != null) { 250 sb.append(" (id=").append(id.getChildUuidString()).append(")"); 251 String parentId = id.getParentUuidString(); 252 if (parentId != null) { 253 sb.append(" is "); 254 sb.append(Inflector.getInstance().ordinalize(indexInParent)); 255 sb.append(" child of "); 256 sb.append(parentId); 257 sb.append(" in workspace "); 258 sb.append(id.getWorkspaceId()); 259 } else { 260 sb.append(" is root in workspace "); 261 sb.append(id.getWorkspaceId()); 262 } 263 } 264 return sb.toString(); 265 } 266 267 @SuppressWarnings( "unchecked" ) 268 public static void adjustSnsIndexesAndIndexesAfterRemoving( EntityManager entities, 269 Long workspaceId, 270 String uuidParent, 271 String childName, 272 long childNamespaceIndex, 273 int childIndex ) { 274 // Decrement the 'indexInParent' index values for all nodes above the previously removed sibling ... 275 Query query = entities.createNamedQuery("ChildEntity.findChildrenAfterIndexUnderParent"); 276 query.setParameter("workspaceId", workspaceId); 277 query.setParameter("parentUuidString", uuidParent); 278 query.setParameter("afterIndex", childIndex); 279 for (ChildEntity entity : (List<ChildEntity>)query.getResultList()) { 280 // Decrement the index in parent ... 281 entity.setIndexInParent(entity.getIndexInParent() - 1); 282 if (entity.getChildName().equals(childName) && entity.getChildNamespace().getId() == childNamespaceIndex) { 283 // The name matches, so decrement the SNS index ... 284 entity.setSameNameSiblingIndex(entity.getSameNameSiblingIndex() - 1); 285 } 286 } 287 } 288 289 }