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    }