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( Children original, 082 Name additionalChildName, 083 UUID childUuid, 084 PathFactory pathFactory ) { 085 this(original); 086 add(additionalChildName, childUuid, pathFactory); 087 } 088 089 /** 090 * Utility method that adds a child with the supplied name. This method is not exposed publicly, ensuring that this class 091 * remains publicly immutable. Subclasses that use this method (in places other than constructors) will no longer be 092 * {@link Immutable immutable}. 093 * 094 * @param additionalChildName 095 * @param childUuid 096 * @param pathFactory 097 * @return the child node that was just added; never null 098 */ 099 protected ChildNode add( Name additionalChildName, 100 UUID childUuid, 101 PathFactory pathFactory ) { 102 ChildNode existing = this.childrenByUuid.get(childUuid); 103 if (existing != null) return existing; 104 105 List<ChildNode> childrenWithName = this.childrenByName.get(additionalChildName); 106 Path.Segment segment = pathFactory.createSegment(additionalChildName, childrenWithName.size() + 1); 107 ChildNode additionalChild = new ChildNode(childUuid, segment); 108 this.childrenByName.put(additionalChildName, additionalChild); 109 this.childrenByUuid.put(childUuid, additionalChild); 110 return additionalChild; 111 } 112 113 /** 114 * {@inheritDoc} 115 * 116 * @see org.jboss.dna.jcr.cache.Children#size() 117 */ 118 public int size() { 119 return childrenByName.size(); 120 } 121 122 /** 123 * {@inheritDoc} 124 * 125 * @see java.lang.Iterable#iterator() 126 */ 127 public Iterator<ChildNode> iterator() { 128 return new ReadOnlyIterator<ChildNode>(this.childrenByName.values().iterator()); 129 } 130 131 /** 132 * {@inheritDoc} 133 * 134 * @see org.jboss.dna.jcr.cache.Children#getParentUuid() 135 */ 136 public UUID getParentUuid() { 137 return parentUuid; 138 } 139 140 /** 141 * {@inheritDoc} 142 * 143 * @see org.jboss.dna.jcr.cache.Children#getChild(java.util.UUID) 144 */ 145 public ChildNode getChild( UUID uuid ) { 146 return this.childrenByUuid.get(uuid); 147 } 148 149 /** 150 * {@inheritDoc} 151 * 152 * @see org.jboss.dna.jcr.cache.Children#getChild(org.jboss.dna.graph.property.Path.Segment) 153 */ 154 public ChildNode getChild( Segment segment ) { 155 List<ChildNode> childrenWithName = this.childrenByName.get(segment.getName()); 156 int snsIndex = segment.getIndex(); 157 if (childrenWithName.size() < snsIndex) return null; 158 return childrenWithName.get(snsIndex - 1); 159 } 160 161 /** 162 * {@inheritDoc} 163 * 164 * @see org.jboss.dna.jcr.cache.Children#getChildren(org.jboss.dna.graph.property.Name) 165 */ 166 public Iterator<ChildNode> getChildren( Name name ) { 167 return new ReadOnlyIterator<ChildNode>(this.childrenByName.get(name).iterator()); 168 } 169 170 /** 171 * {@inheritDoc} 172 * 173 * @see org.jboss.dna.jcr.cache.Children#getCountOfSameNameSiblingsWithName(org.jboss.dna.graph.property.Name) 174 */ 175 public int getCountOfSameNameSiblingsWithName( Name name ) { 176 return this.childrenByName.get(name).size(); 177 } 178 179 /** 180 * {@inheritDoc} 181 * 182 * @see org.jboss.dna.jcr.cache.InternalChildren#with(org.jboss.dna.graph.property.Name, java.util.UUID, 183 * org.jboss.dna.graph.property.PathFactory) 184 */ 185 public ChangedChildren with( Name newChildName, 186 UUID newChildUuid, 187 PathFactory pathFactory ) { 188 // Create a mutable version ... 189 ChangedChildren newChildren = new ChangedChildren(this); 190 return newChildren.with(newChildName, newChildUuid, pathFactory); 191 } 192 193 /** 194 * {@inheritDoc} 195 * 196 * @see org.jboss.dna.jcr.cache.InternalChildren#without(java.util.UUID, org.jboss.dna.graph.property.PathFactory) 197 */ 198 public ChangedChildren without( UUID childUuid, 199 PathFactory pathFactory ) { 200 if (this.childrenByUuid.containsKey(childUuid) && this.size() == 1) { 201 return new ChangedChildren(this.parentUuid); 202 } 203 ChangedChildren newChildren = new ChangedChildren(this); 204 return newChildren.without(childUuid, pathFactory); 205 } 206 207 /** 208 * {@inheritDoc} 209 * 210 * @see java.lang.Object#toString() 211 */ 212 @Override 213 public String toString() { 214 StringBuilder sb = new StringBuilder(); 215 boolean first = true; 216 for (ChildNode child : childrenByName.values()) { 217 if (!first) sb.append(", "); 218 else first = false; 219 sb.append(child.getName()).append('[').append(child.getSnsIndex()).append(']'); 220 } 221 return sb.toString(); 222 } 223 }