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.LinkedList;
030    import java.util.List;
031    import java.util.NoSuchElementException;
032    import net.jcip.annotations.NotThreadSafe;
033    import org.jboss.dna.common.CommonI18n;
034    import org.jboss.dna.common.text.TextEncoder;
035    import org.jboss.dna.common.util.CheckArg;
036    import org.jboss.dna.graph.GraphI18n;
037    import org.jboss.dna.graph.property.InvalidPathException;
038    import org.jboss.dna.graph.property.NamespaceRegistry;
039    import org.jboss.dna.graph.property.Path;
040    
041    /**
042     * An abstract foundation for different {@link Path} implementations. This class does not manage any of the {@link Path}'s state,
043     * but it does provide implementations for most of the methods based upon a few abstract methods. For example, any implementaton
044     * that requires the {@link Path.Segment path's segments} are written to use the {@link #iterator()}, since that is likely more
045     * efficient for the majority of implementations.
046     * 
047     * @author Randall Hauch
048     */
049    public abstract class AbstractPath implements Path {
050    
051        /**
052         * The initial serializable version. Version {@value}
053         */
054        private static final long serialVersionUID = 1L;
055    
056        public static final Path SELF_PATH = new BasicPath(Collections.singletonList(Path.SELF_SEGMENT), false);
057    
058        protected static Iterator<Path.Segment> EMPTY_PATH_ITERATOR = new Iterator<Segment>() {
059            /**
060             * {@inheritDoc}
061             * 
062             * @see java.util.Iterator#hasNext()
063             */
064            public boolean hasNext() {
065                return false;
066            }
067    
068            /**
069             * {@inheritDoc}
070             * 
071             * @see java.util.Iterator#next()
072             */
073            public Segment next() {
074                throw new NoSuchElementException();
075            }
076    
077            /**
078             * {@inheritDoc}
079             * 
080             * @see java.util.Iterator#remove()
081             */
082            public void remove() {
083                throw new UnsupportedOperationException();
084            }
085        };
086    
087        @NotThreadSafe
088        protected static class SingleIterator<T> implements Iterator<T> {
089            private T value;
090    
091            protected SingleIterator( T value ) {
092                this.value = value;
093            }
094    
095            /**
096             * {@inheritDoc}
097             * 
098             * @see java.util.Iterator#hasNext()
099             */
100            public boolean hasNext() {
101                return value != null;
102            }
103    
104            /**
105             * {@inheritDoc}
106             * 
107             * @see java.util.Iterator#next()
108             */
109            public T next() {
110                if (value == null) throw new NoSuchElementException();
111                T next = value;
112                value = null;
113                return next;
114            }
115    
116            /**
117             * {@inheritDoc}
118             * 
119             * @see java.util.Iterator#remove()
120             */
121            public void remove() {
122                throw new UnsupportedOperationException();
123            }
124        }
125    
126        private transient int hc = 0;
127    
128        protected boolean isNormalized( List<Segment> segments ) {
129            boolean nonParentReference = false;
130            boolean first = isAbsolute(); // only care about first one when it's absolute
131            for (Segment segment : segments) {
132                if (segment.isSelfReference()) return false;
133                if (segment.isParentReference()) {
134                    if (nonParentReference || first) return false;
135                } else {
136                    nonParentReference = true;
137                }
138                first = false;
139            }
140            return true;
141        }
142    
143        /**
144         * {@inheritDoc}
145         * 
146         * @see org.jboss.dna.graph.property.Path#getCanonicalPath()
147         */
148        public Path getCanonicalPath() {
149            if (!this.isAbsolute()) {
150                String msg = GraphI18n.pathIsNotAbsolute.text(this);
151                throw new InvalidPathException(msg);
152            }
153            if (this.isNormalized()) return this;
154            return this.getNormalizedPath();
155        }
156    
157        /**
158         * {@inheritDoc}
159         */
160        public Path getCommonAncestor( Path that ) {
161            CheckArg.isNotNull(that, "that");
162            if (that.isRoot()) return that;
163            Path normalizedPath = this.getNormalizedPath();
164            int lastIndex = 0;
165            Iterator<Segment> thisIter = normalizedPath.iterator();
166            Iterator<Segment> thatIter = that.getNormalizedPath().iterator();
167            while (thisIter.hasNext() && thatIter.hasNext()) {
168                Segment thisSeg = thisIter.next();
169                Segment thatSeg = thatIter.next();
170                if (thisSeg.equals(thatSeg)) {
171                    ++lastIndex;
172                } else {
173                    break;
174                }
175            }
176            if (lastIndex == 0) return RootPath.INSTANCE;
177            return normalizedPath.subpath(0, lastIndex);
178        }
179    
180        /**
181         * {@inheritDoc}
182         */
183        public Path.Segment getLastSegment() {
184            return this.getSegmentsList().get(size() - 1);
185        }
186    
187        /**
188         * {@inheritDoc}
189         * 
190         * @see org.jboss.dna.graph.property.Path#getParent()
191         */
192        public Path getParent() {
193            return getAncestor(1);
194        }
195    
196        /**
197         * {@inheritDoc}
198         */
199        public Segment getSegment( int index ) {
200            CheckArg.isNonNegative(index, "index");
201            return this.getSegmentsList().get(index);
202        }
203    
204        /**
205         * {@inheritDoc}
206         */
207        public Segment[] getSegmentsArray() {
208            // By default, make a new array every time since arrays are mutable, and use the iterator
209            // since that is probably more efficient than creating a list ...
210            Segment[] result = new Path.Segment[size()];
211            int i = 0;
212            for (Segment segment : this) {
213                result[i] = segment;
214                ++i;
215            }
216            return result;
217        }
218    
219        /**
220         * {@inheritDoc}
221         */
222        public Path getNormalizedPath() {
223            if (this.isNormalized()) return this;
224            LinkedList<Segment> newSegments = new LinkedList<Segment>();
225            for (Segment segment : this) {
226                if (segment.isSelfReference()) continue;
227                if (segment.isParentReference()) {
228                    if (newSegments.isEmpty()) {
229                        if (this.isAbsolute()) {
230                            throw new InvalidPathException(CommonI18n.pathCannotBeNormalized.text(this));
231                        }
232                    } else if (!newSegments.getLast().isParentReference()) {
233                        newSegments.removeLast();
234                        continue;
235                    }
236                }
237                newSegments.add(segment);
238            }
239            if (newSegments.isEmpty()) {
240                if (this.isAbsolute()) return RootPath.INSTANCE;
241                // Otherwise relative and it had contained nothing but self references ...
242                return SELF_PATH;
243            }
244            return new BasicPath(newSegments, this.isAbsolute());
245        }
246    
247        /**
248         * {@inheritDoc}
249         */
250        public String getString() {
251            return doGetString(null, null, null);
252        }
253    
254        /**
255         * {@inheritDoc}
256         */
257        public String getString( TextEncoder encoder ) {
258            return doGetString(null, encoder, null);
259        }
260    
261        /**
262         * {@inheritDoc}
263         */
264        public String getString( NamespaceRegistry namespaceRegistry ) {
265            CheckArg.isNotNull(namespaceRegistry, "namespaceRegistry");
266            return doGetString(namespaceRegistry, null, null);
267        }
268    
269        /**
270         * {@inheritDoc}
271         */
272        public String getString( NamespaceRegistry namespaceRegistry,
273                                 TextEncoder encoder ) {
274            CheckArg.isNotNull(namespaceRegistry, "namespaceRegistry");
275            return doGetString(namespaceRegistry, encoder, null);
276        }
277    
278        /**
279         * {@inheritDoc}
280         * 
281         * @see org.jboss.dna.graph.property.Path#getString(org.jboss.dna.graph.property.NamespaceRegistry,
282         *      org.jboss.dna.common.text.TextEncoder, org.jboss.dna.common.text.TextEncoder)
283         */
284        public String getString( NamespaceRegistry namespaceRegistry,
285                                 TextEncoder encoder,
286                                 TextEncoder delimiterEncoder ) {
287            return doGetString(namespaceRegistry, encoder, delimiterEncoder);
288        }
289    
290        /**
291         * Method that creates the string representation. This method works two different ways depending upon whether the namespace
292         * registry is provided.
293         * 
294         * @param namespaceRegistry
295         * @param encoder
296         * @param delimiterEncoder
297         * @return this path as a string
298         */
299        protected String doGetString( NamespaceRegistry namespaceRegistry,
300                                      TextEncoder encoder,
301                                      TextEncoder delimiterEncoder ) {
302            if (encoder == null) encoder = DEFAULT_ENCODER;
303            final String delimiter = delimiterEncoder != null ? delimiterEncoder.encode(DELIMITER_STR) : DELIMITER_STR;
304    
305            // Since the segments are immutable, this code need not be synchronized because concurrent threads
306            // may just compute the same value (with no harm done)
307            StringBuilder sb = new StringBuilder();
308            if (this.isAbsolute()) sb.append(delimiter);
309            boolean first = true;
310            for (Segment segment : this) {
311                if (first) {
312                    first = false;
313                } else {
314                    sb.append(delimiter);
315                }
316                assert segment != null;
317                sb.append(segment.getString(namespaceRegistry, encoder, delimiterEncoder));
318            }
319            String result = sb.toString();
320            // Save the result to the internal string if this the default encoder is used.
321            // This is not synchronized, but it's okay
322            return result;
323        }
324    
325        /**
326         * {@inheritDoc}
327         */
328        public boolean hasSameAncestor( Path that ) {
329            CheckArg.isNotNull(that, "that");
330            if (that.size() != this.size()) return false;
331            if (this.size() == 1) return true; // both nodes are just under the root
332            for (int i = this.size() - 2; i >= 0; --i) {
333                Path.Segment thisSegment = this.getSegment(i);
334                Path.Segment thatSegment = that.getSegment(i);
335                if (!thisSegment.equals(thatSegment)) return false;
336            }
337            return true;
338        }
339    
340        /**
341         * {@inheritDoc}
342         */
343        public boolean isAncestorOf( Path decendant ) {
344            CheckArg.isNotNull(decendant, "that");
345            if (this == decendant) return false;
346            if (this.size() >= decendant.size()) return false;
347    
348            Iterator<Path.Segment> thisIter = this.iterator();
349            Iterator<Path.Segment> thatIter = decendant.iterator();
350            while (thisIter.hasNext()) {
351                Path.Segment thisSeg = thisIter.next();
352                Path.Segment thatSeg = thatIter.next();
353                if (!thisSeg.equals(thatSeg)) return false;
354            }
355            return true;
356        }
357    
358        /**
359         * {@inheritDoc}
360         * 
361         * @see org.jboss.dna.graph.property.Path#isAtOrBelow(org.jboss.dna.graph.property.Path)
362         */
363        public boolean isAtOrBelow( Path other ) {
364            CheckArg.isNotNull(other, "other");
365            if (this == other) return true;
366            if (other.isRoot()) return true;
367            if (other.size() > this.size()) return false;
368            Iterator<Segment> thisIter = iterator();
369            Iterator<Segment> thatIter = other.iterator();
370            while (thisIter.hasNext() && thatIter.hasNext()) {
371                if (!thisIter.next().equals(thatIter.next())) return false;
372            }
373            if (thatIter.hasNext()) return false; // The other still has segments, but this doesn't
374            return true;
375        }
376    
377        /**
378         * {@inheritDoc}
379         * 
380         * @see org.jboss.dna.graph.property.Path#isAtOrAbove(org.jboss.dna.graph.property.Path)
381         */
382        public boolean isAtOrAbove( Path other ) {
383            CheckArg.isNotNull(other, "other");
384            if (this == other) return true;
385            if (this.size() > other.size()) return false;
386            Iterator<Segment> thisIter = iterator();
387            Iterator<Segment> thatIter = other.iterator();
388            while (thisIter.hasNext() && thatIter.hasNext()) {
389                if (!thisIter.next().equals(thatIter.next())) return false;
390            }
391            if (thisIter.hasNext()) return false; // This still has segments, but other doesn't
392            return true;
393        }
394    
395        /**
396         * {@inheritDoc}
397         */
398        public boolean isDecendantOf( Path ancestor ) {
399            CheckArg.isNotNull(ancestor, "ancestor");
400            return ancestor.isAncestorOf(this);
401        }
402    
403        /**
404         * {@inheritDoc}
405         */
406        public boolean isSameAs( Path other ) {
407            return other != null && this.compareTo(other) == 0;
408        }
409    
410        /**
411         * {@inheritDoc}
412         */
413        public Iterator<Segment> iterator() {
414            return getSegmentsList().iterator();
415        }
416    
417        /**
418         * {@inheritDoc}
419         * 
420         * @see org.jboss.dna.graph.property.Path#pathsFromRoot()
421         */
422        public Iterator<Path> pathsFromRoot() {
423            LinkedList<Path> paths = new LinkedList<Path>();
424            Path path = this;
425            while (path != null) {
426                paths.addFirst(path);
427                if (path.isRoot()) break;
428                path = path.getParent();
429            }
430            return paths.iterator();
431        }
432    
433        /**
434         * {@inheritDoc}
435         */
436        public Path relativeTo( Path startingPath ) {
437            CheckArg.isNotNull(startingPath, "to");
438            if (!this.isAbsolute()) {
439                String msg = GraphI18n.pathIsNotAbsolute.text(this);
440                throw new InvalidPathException(msg);
441            }
442            if (!startingPath.isAbsolute()) {
443                String msg = GraphI18n.pathIsNotAbsolute.text(startingPath);
444                throw new InvalidPathException(msg);
445            }
446    
447            // Count the number of segments up to the common ancestor (relative path is what remains) ...
448            int lengthOfCommonAncestor = 0;
449            Iterator<Segment> thisIter = this.getNormalizedPath().iterator();
450            Iterator<Segment> toIter = startingPath.getNormalizedPath().iterator();
451            while (thisIter.hasNext() && toIter.hasNext()) {
452                Segment thisSeg = thisIter.next();
453                Segment toSeg = toIter.next();
454                if (thisSeg.equals(toSeg)) {
455                    ++lengthOfCommonAncestor;
456                } else {
457                    break;
458                }
459            }
460            // Create the relative path, starting with parent references to the common ancestor ...
461            int numberOfParentReferences = startingPath.size() - lengthOfCommonAncestor;
462            List<Segment> relativeSegments = new ArrayList<Segment>();
463            for (int i = 0; i != numberOfParentReferences; ++i) {
464                relativeSegments.add(Path.PARENT_SEGMENT);
465            }
466            // Add the segments of this path from the common ancestor ...
467            for (int i = lengthOfCommonAncestor; i < this.size(); ++i) {
468                relativeSegments.add(getSegment(i));
469            }
470            if (relativeSegments.isEmpty()) {
471                relativeSegments.add(Path.SELF_SEGMENT);
472            }
473            return new BasicPath(relativeSegments, false);
474        }
475    
476        /**
477         * {@inheritDoc}
478         */
479        public Path resolve( Path relativePath ) {
480            CheckArg.isNotNull(relativePath, "relative path");
481            if (!this.isAbsolute()) {
482                String msg = GraphI18n.pathIsNotAbsolute.text(this);
483                throw new InvalidPathException(msg);
484            }
485            if (relativePath.isAbsolute()) {
486                String msg = GraphI18n.pathIsNotRelative.text(relativePath);
487                throw new InvalidPathException(msg);
488            }
489            // If the relative path is the self or parent reference ...
490            relativePath = relativePath.getNormalizedPath();
491            if (relativePath.size() == 1) {
492                Segment onlySegment = relativePath.getSegment(0);
493                if (onlySegment.isSelfReference()) return this;
494                if (onlySegment.isParentReference()) return this.getParent();
495            }
496            List<Segment> segments = new ArrayList<Segment>(this.size() + relativePath.size());
497            for (Segment segment : this) {
498                segments.add(segment);
499            }
500            for (Segment segment : relativePath) {
501                segments.add(segment);
502            }
503            return new BasicPath(segments, true).getNormalizedPath();
504        }
505    
506        /**
507         * {@inheritDoc}
508         */
509        public Path resolveAgainst( Path absolutePath ) {
510            CheckArg.isNotNull(absolutePath, "absolute path");
511            return absolutePath.resolve(this);
512        }
513    
514        /**
515         * {@inheritDoc}
516         */
517        public Path subpath( int beginIndex ) {
518            return subpath(beginIndex, size());
519        }
520    
521        /**
522         * {@inheritDoc}
523         */
524        public Path subpath( int beginIndex,
525                             int endIndex ) {
526            CheckArg.isNonNegative(beginIndex, "beginIndex");
527            CheckArg.isNonNegative(endIndex, "endIndex");
528            int size = size();
529            if (beginIndex == 0) {
530                if (endIndex == 0) return RootPath.INSTANCE;
531                if (endIndex == size) return this;
532            }
533            if (beginIndex >= size) {
534                throw new IndexOutOfBoundsException(
535                                                    GraphI18n.unableToCreateSubpathBeginIndexGreaterThanOrEqualToSize.text(beginIndex,
536                                                                                                                           size));
537            }
538            if (beginIndex > endIndex) {
539                throw new IndexOutOfBoundsException(
540                                                    GraphI18n.unableToCreateSubpathBeginIndexGreaterThanOrEqualToEndingIndex.text(beginIndex,
541                                                                                                                                  endIndex));
542            }
543            // This reuses the same list, so it's pretty efficient ...
544            return new BasicPath(createSegmentsSubList(beginIndex, endIndex), this.isAbsolute());
545        }
546    
547        protected List<Segment> createSegmentsSubList( int validBeginIndex,
548                                                       int validEndIndex ) {
549            return this.getSegmentsList().subList(validBeginIndex, validEndIndex);
550        }
551    
552        /**
553         * {@inheritDoc}
554         */
555        @Override
556        public int hashCode() {
557            if (hc == 0) {
558                int hashCode = 1;
559                for (Segment segment : this) {
560                    hashCode = 31 * hashCode + segment.hashCode();
561                }
562                hc = hashCode;
563            }
564            return hc;
565        }
566    
567        /**
568         * Method used by {@link AbstractPath#equals(Object)} implementation to quickly get an Iterator over the segments in the
569         * parent.
570         * 
571         * @return the iterator over the segments; never null, but may not have any elements
572         */
573        protected abstract Iterator<Segment> getSegmentsOfParent();
574    
575        /**
576         * {@inheritDoc}
577         */
578        @Override
579        public boolean equals( Object obj ) {
580            if (obj == this) return true;
581            if (obj instanceof Path) {
582                Path that = (Path)obj;
583                // First check whether the paths are roots ...
584                if (this.isRoot()) return that.isRoot();
585                else if (that.isRoot()) return false;
586                // Now check the hash code and size ...
587                if (this.hashCode() != that.hashCode()) return false;
588                if (this.size() != that.size()) return false;
589                // Check the last segments, since these will often differ anyway ...
590                if (!this.getLastSegment().equals(that.getLastSegment())) return false;
591                if (this.size() == 1) return true;
592                // Check the rest of the names ...
593                Iterator<Segment> thisIter = that instanceof AbstractPath ? this.getSegmentsOfParent() : this.iterator();
594                Iterator<Segment> thatIter = that instanceof AbstractPath ? ((AbstractPath)that).getSegmentsOfParent() : that.iterator();
595                while (thisIter.hasNext()) {
596                    Segment thisSegment = thisIter.next();
597                    Segment thatSegment = thatIter.next();
598                    if (!thisSegment.equals(thatSegment)) return false;
599                }
600                return true;
601            }
602            return false;
603        }
604    
605        /**
606         * {@inheritDoc}
607         */
608        public int compareTo( Path that ) {
609            if (this == that) return 0;
610            Iterator<Segment> thisIter = getSegmentsList().iterator();
611            Iterator<Segment> thatIter = that.iterator();
612            while (thisIter.hasNext() && thatIter.hasNext()) {
613                Segment thisSegment = thisIter.next();
614                Segment thatSegment = thatIter.next();
615                int diff = thisSegment.compareTo(thatSegment);
616                if (diff != 0) return diff;
617            }
618            if (thisIter.hasNext()) return 1;
619            if (thatIter.hasNext()) return -1;
620            return 0;
621        }
622    
623        /**
624         * {@inheritDoc}
625         */
626        @Override
627        public String toString() {
628            return getString(Path.NO_OP_ENCODER);
629        }
630    }