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 * Unless otherwise indicated, all code in JBoss DNA is licensed 010 * 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.jcr.cache; 025 026 import java.util.HashMap; 027 import java.util.Iterator; 028 import java.util.List; 029 import java.util.Map; 030 import java.util.UUID; 031 import net.jcip.annotations.Immutable; 032 import org.jboss.dna.common.collection.ReadOnlyIterator; 033 import org.jboss.dna.graph.Location; 034 import org.jboss.dna.graph.property.Name; 035 import org.jboss.dna.graph.property.Path; 036 import org.jboss.dna.graph.property.PathFactory; 037 import org.jboss.dna.graph.property.Path.Segment; 038 import com.google.common.collect.LinkedListMultimap; 039 import com.google.common.collect.ListMultimap; 040 041 /** 042 * An immutable implementation of {@link Children}. 043 */ 044 @Immutable 045 public class ImmutableChildren implements Children, InternalChildren { 046 protected final UUID parentUuid; 047 protected final Map<UUID, ChildNode> childrenByUuid; 048 protected final ListMultimap<Name, ChildNode> childrenByName; 049 050 public ImmutableChildren( UUID parentUuid, 051 Iterable<Location> children ) { 052 this(parentUuid); 053 for (Location childLocation : children) { 054 UUID childUuid = childLocation.getUuid(); 055 Path.Segment segment = childLocation.getPath().getLastSegment(); 056 Name name = segment.getName(); 057 ChildNode child = new ChildNode(childUuid, segment); 058 this.childrenByName.put(name, child); 059 this.childrenByUuid.put(childUuid, child); 060 } 061 } 062 063 public ImmutableChildren( UUID parentUuid ) { 064 this.parentUuid = parentUuid; 065 this.childrenByUuid = new HashMap<UUID, ChildNode>(); 066 this.childrenByName = new LinkedListMultimap<Name, ChildNode>(); 067 } 068 069 protected ImmutableChildren( Children original ) { 070 this.parentUuid = original.getParentUuid(); 071 this.childrenByUuid = new HashMap<UUID, ChildNode>(); 072 this.childrenByName = new LinkedListMultimap<Name, ChildNode>(); 073 Iterator<ChildNode> iter = original.iterator(); 074 while (iter.hasNext()) { 075 ChildNode child = iter.next(); 076 this.childrenByName.put(child.getName(), child); 077 this.childrenByUuid.put(child.getUuid(), child); 078 } 079 } 080 081 protected ImmutableChildren( ImmutableChildren original, 082 Name additionalChildName, 083 Path.Segment beforeChild, 084 UUID childUuid, 085 PathFactory pathFactory ) { 086 assert beforeChild != null; 087 088 this.parentUuid = original.getParentUuid(); 089 this.childrenByUuid = new HashMap<UUID, ChildNode>(); 090 this.childrenByName = new LinkedListMultimap<Name, ChildNode>(); 091 092 int snsIndex = 1; 093 boolean found = false; 094 ChildNode additionalChild = null; 095 for (ChildNode child : original.childrenByName.values()) { 096 this.childrenByUuid.put(child.getUuid(), child); 097 if (beforeChild.equals(child.getSegment())) { 098 Path.Segment segment = pathFactory.createSegment(additionalChildName, snsIndex++); 099 additionalChild = new ChildNode(childUuid, segment); 100 childrenByName.put(child.getName(), additionalChild); 101 found = true; 102 } 103 104 if (found &&(child.getName().equals(additionalChildName))) { 105 Path.Segment newSegment = pathFactory.createSegment(additionalChildName, snsIndex++); 106 childrenByName.put(child.getName(), child.with(newSegment)); 107 } 108 else { 109 childrenByName.put(child.getName(), child); 110 } 111 } 112 113 assert additionalChild != null; 114 this.childrenByUuid.put(childUuid, additionalChild); 115 } 116 117 /** 118 * Utility method that adds a child with the supplied name. This method is not exposed publicly, ensuring that this class 119 * remains publicly immutable. Subclasses that use this method (in places other than constructors) will no longer be 120 * {@link Immutable immutable}. 121 * 122 * @param additionalChildName 123 * @param childUuid 124 * @param pathFactory 125 * @return the child node that was just added; never null 126 */ 127 protected ChildNode add( Name additionalChildName, 128 UUID childUuid, 129 PathFactory pathFactory ) { 130 ChildNode existing = this.childrenByUuid.get(childUuid); 131 if (existing != null) return existing; 132 133 List<ChildNode> childrenWithName = this.childrenByName.get(additionalChildName); 134 Path.Segment segment = pathFactory.createSegment(additionalChildName, childrenWithName.size() + 1); 135 ChildNode additionalChild = new ChildNode(childUuid, segment); 136 this.childrenByName.put(additionalChildName, additionalChild); 137 this.childrenByUuid.put(childUuid, additionalChild); 138 return additionalChild; 139 } 140 141 /** 142 * {@inheritDoc} 143 * 144 * @see org.jboss.dna.jcr.cache.Children#size() 145 */ 146 public int size() { 147 return childrenByName.size(); 148 } 149 150 /** 151 * {@inheritDoc} 152 * 153 * @see java.lang.Iterable#iterator() 154 */ 155 public Iterator<ChildNode> iterator() { 156 return new ReadOnlyIterator<ChildNode>(this.childrenByName.values().iterator()); 157 } 158 159 /** 160 * {@inheritDoc} 161 * 162 * @see org.jboss.dna.jcr.cache.Children#getParentUuid() 163 */ 164 public UUID getParentUuid() { 165 return parentUuid; 166 } 167 168 /** 169 * {@inheritDoc} 170 * 171 * @see org.jboss.dna.jcr.cache.Children#getChild(java.util.UUID) 172 */ 173 public ChildNode getChild( UUID uuid ) { 174 return this.childrenByUuid.get(uuid); 175 } 176 177 /** 178 * {@inheritDoc} 179 * 180 * @see org.jboss.dna.jcr.cache.Children#getChild(org.jboss.dna.graph.property.Path.Segment) 181 */ 182 public ChildNode getChild( Segment segment ) { 183 List<ChildNode> childrenWithName = this.childrenByName.get(segment.getName()); 184 int snsIndex = segment.getIndex(); 185 if (childrenWithName.size() < snsIndex) return null; 186 return childrenWithName.get(snsIndex - 1); 187 } 188 189 /** 190 * {@inheritDoc} 191 * 192 * @see org.jboss.dna.jcr.cache.Children#getChildren(org.jboss.dna.graph.property.Name) 193 */ 194 public Iterator<ChildNode> getChildren( Name name ) { 195 return new ReadOnlyIterator<ChildNode>(this.childrenByName.get(name).iterator()); 196 } 197 198 /** 199 * {@inheritDoc} 200 * 201 * @see org.jboss.dna.jcr.cache.Children#getCountOfSameNameSiblingsWithName(org.jboss.dna.graph.property.Name) 202 */ 203 public int getCountOfSameNameSiblingsWithName( Name name ) { 204 return this.childrenByName.get(name).size(); 205 } 206 207 /** 208 * {@inheritDoc} 209 * 210 * @see org.jboss.dna.jcr.cache.InternalChildren#with(org.jboss.dna.graph.property.Name, java.util.UUID, 211 * org.jboss.dna.graph.property.PathFactory) 212 */ 213 public ChangedChildren with( Name newChildName, 214 UUID newChildUuid, 215 PathFactory pathFactory ) { 216 // Create a mutable version ... 217 ChangedChildren newChildren = new ChangedChildren(this); 218 return newChildren.with(newChildName, newChildUuid, pathFactory); 219 } 220 221 /** 222 * {@inheritDoc} 223 * 224 * @see org.jboss.dna.jcr.cache.InternalChildren#without(java.util.UUID, org.jboss.dna.graph.property.PathFactory) 225 */ 226 public ChangedChildren without( UUID childUuid, 227 PathFactory pathFactory ) { 228 if (this.childrenByUuid.containsKey(childUuid) && this.size() == 1) { 229 return new ChangedChildren(this.parentUuid); 230 } 231 ChangedChildren newChildren = new ChangedChildren(this); 232 return newChildren.without(childUuid, pathFactory); 233 } 234 235 /** 236 * {@inheritDoc} 237 * 238 * @see java.lang.Object#toString() 239 */ 240 @Override 241 public String toString() { 242 StringBuilder sb = new StringBuilder(); 243 boolean first = true; 244 for (ChildNode child : childrenByName.values()) { 245 if (!first) sb.append(", "); 246 else first = false; 247 sb.append(child.getName()).append('[').append(child.getSnsIndex()).append(']'); 248 } 249 return sb.toString(); 250 } 251 }