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.graph.property.basic; 025 026 import java.util.ArrayList; 027 import java.util.Collections; 028 import java.util.Iterator; 029 import java.util.List; 030 import java.util.NoSuchElementException; 031 import org.jboss.dna.common.collection.ImmutableAppendedList; 032 import org.jboss.dna.common.util.CheckArg; 033 import org.jboss.dna.graph.property.Path; 034 035 /** 036 * Implementation of a {@link Path} that has the information for the last segment but that points to another Path for the parent 037 * information. 038 * 039 * @author Randall Hauch 040 */ 041 public class ChildPath extends AbstractPath { 042 043 /** 044 * The serializable version. Version {@value} 045 */ 046 private static final long serialVersionUID = 1L; 047 048 private final Path parent; 049 private final Path.Segment child; 050 private final int size; 051 private transient List<Segment> cachedSegmentList; 052 053 public ChildPath( Path parent, 054 Path.Segment child ) { 055 assert parent != null; 056 assert child != null; 057 this.parent = parent; 058 this.child = child; 059 this.size = this.parent.size() + 1; 060 } 061 062 /** 063 * {@inheritDoc} 064 * 065 * @see org.jboss.dna.graph.property.Path#getAncestor(int) 066 */ 067 public Path getAncestor( int degree ) { 068 CheckArg.isNonNegative(degree, "degree"); 069 if (degree == 0) return this; 070 if (degree == 1) return parent; 071 return parent.getAncestor(degree - 1); 072 } 073 074 /** 075 * {@inheritDoc} 076 * 077 * @see org.jboss.dna.graph.property.basic.AbstractPath#getSegmentsOfParent() 078 */ 079 @Override 080 protected Iterator<Segment> getSegmentsOfParent() { 081 return parent.iterator(); 082 } 083 084 /** 085 * {@inheritDoc} 086 * 087 * @see org.jboss.dna.graph.property.Path#getLastSegment() 088 */ 089 @Override 090 public Segment getLastSegment() { 091 return child; 092 } 093 094 /** 095 * {@inheritDoc} 096 * 097 * @see org.jboss.dna.graph.property.Path#getParent() 098 */ 099 @Override 100 public Path getParent() { 101 return parent; 102 } 103 104 /** 105 * {@inheritDoc} 106 * 107 * @see org.jboss.dna.graph.property.Path#getSegment(int) 108 */ 109 @Override 110 public Segment getSegment( int index ) { 111 if (index == (size - 1)) return child; 112 return parent.getSegment(index); 113 } 114 115 /** 116 * {@inheritDoc} 117 * 118 * @see org.jboss.dna.graph.property.Path#getSegmentsList() 119 */ 120 public List<Segment> getSegmentsList() { 121 if (cachedSegmentList == null) { 122 // No need to synchronize, since this is idempotent and thus the list will be as well 123 List<Segment> segments = null; 124 if (parent.isRoot()) { 125 segments = Collections.singletonList(child); // already immutable 126 } else if (size < 4) { 127 segments = new ArrayList<Segment>(size); 128 for (Segment segment : parent) { 129 segments.add(segment); 130 } 131 segments.add(child); 132 segments = Collections.unmodifiableList(segments); 133 } else { 134 segments = new ImmutableAppendedList<Segment>(parent.getSegmentsList(), child); 135 } 136 cachedSegmentList = segments; 137 } 138 return cachedSegmentList; 139 } 140 141 /** 142 * {@inheritDoc} 143 * 144 * @see org.jboss.dna.graph.property.Path#hasSameAncestor(org.jboss.dna.graph.property.Path) 145 */ 146 @Override 147 public boolean hasSameAncestor( Path that ) { 148 CheckArg.isNotNull(that, "that"); 149 if (parent.equals(that.getParent())) return true; 150 return super.hasSameAncestor(that); 151 } 152 153 /** 154 * {@inheritDoc} 155 * 156 * @see org.jboss.dna.graph.property.Path#isAbsolute() 157 */ 158 public boolean isAbsolute() { 159 return parent.isAbsolute(); 160 } 161 162 /** 163 * {@inheritDoc} 164 * 165 * @see org.jboss.dna.graph.property.Path#isAtOrBelow(org.jboss.dna.graph.property.Path) 166 */ 167 @Override 168 public boolean isAtOrBelow( Path other ) { 169 if (this == other || parent == other) return true; 170 return super.isAtOrBelow(other); 171 } 172 173 /** 174 * {@inheritDoc} 175 * 176 * @see org.jboss.dna.graph.property.Path#isDecendantOf(org.jboss.dna.graph.property.Path) 177 */ 178 @Override 179 public boolean isDecendantOf( Path ancestor ) { 180 if (parent == ancestor) return true; // same instance 181 return parent.isAtOrBelow(ancestor); 182 } 183 184 /** 185 * {@inheritDoc} 186 * 187 * @see org.jboss.dna.graph.property.Path#isNormalized() 188 */ 189 public boolean isNormalized() { 190 if (child.isSelfReference()) return false; 191 if (!parent.isNormalized()) return false; 192 // Otherwise, the parent is normalized, so this child will be normalized if this child is not a parent reference ... 193 if (!child.isParentReference()) return true; 194 // The path ends with a parent reference. It is normalized only if all other path segments are parent references ... 195 for (Path.Segment segment : parent) { 196 if (!segment.isParentReference()) return false; 197 } 198 return true; 199 } 200 201 /** 202 * {@inheritDoc} 203 * 204 * @see org.jboss.dna.graph.property.Path#isRoot() 205 */ 206 public boolean isRoot() { 207 if (child.isParentReference()) return parent.isRoot(); 208 return false; 209 } 210 211 /** 212 * {@inheritDoc} 213 * 214 * @see org.jboss.dna.graph.property.Path#iterator() 215 */ 216 @Override 217 @SuppressWarnings( "synthetic-access" ) 218 public Iterator<Segment> iterator() { 219 if (parent.isRoot()) { 220 return new Iterator<Segment>() { 221 boolean finished = false; 222 223 public boolean hasNext() { 224 return !finished; 225 } 226 227 public Segment next() { 228 if (finished) throw new NoSuchElementException(); 229 finished = true; 230 return ChildPath.this.child; 231 } 232 233 public void remove() { 234 throw new UnsupportedOperationException(); 235 } 236 }; 237 } 238 final Iterator<Segment> parentIterator = parent.iterator(); 239 return new Iterator<Segment>() { 240 boolean finished = false; 241 242 public boolean hasNext() { 243 return parentIterator.hasNext() || !finished; 244 } 245 246 public Segment next() { 247 if (parentIterator.hasNext()) return parentIterator.next(); 248 if (finished) throw new NoSuchElementException(); 249 finished = true; 250 return ChildPath.this.child; 251 } 252 253 public void remove() { 254 throw new UnsupportedOperationException(); 255 } 256 }; 257 } 258 259 /** 260 * {@inheritDoc} 261 * 262 * @see org.jboss.dna.graph.property.Path#size() 263 */ 264 public int size() { 265 return size; 266 } 267 268 /** 269 * {@inheritDoc} 270 * 271 * @see org.jboss.dna.graph.property.Path#subpath(int, int) 272 */ 273 @Override 274 public Path subpath( int beginIndex, 275 int endIndex ) { 276 if (beginIndex == 0 && endIndex == (size - 1)) return parent; 277 return super.subpath(beginIndex, endIndex); 278 } 279 280 }