001    /*
002     * JBoss, Home of Professional Open Source.
003     * Copyright 2008, Red Hat Middleware LLC, and individual contributors
004     * as indicated by the @author tags. See the copyright.txt file in the
005     * distribution for a full listing of individual contributors.
006     *
007     * This is free software; you can redistribute it and/or modify it
008     * under the terms of the GNU Lesser General Public License as
009     * published by the Free Software Foundation; either version 2.1 of
010     * the License, or (at your option) any later version.
011     *
012     * This software is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015     * Lesser General Public License for more details.
016     *
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this software; if not, write to the Free
019     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021     */
022    package org.jboss.dna.graph.properties.basic;
023    
024    import java.io.InputStream;
025    import java.io.Reader;
026    import java.math.BigDecimal;
027    import java.net.URI;
028    import java.util.ArrayList;
029    import java.util.Calendar;
030    import java.util.Date;
031    import java.util.LinkedList;
032    import java.util.List;
033    import java.util.UUID;
034    import java.util.regex.Pattern;
035    import net.jcip.annotations.Immutable;
036    import org.jboss.dna.common.text.TextDecoder;
037    import org.jboss.dna.common.util.CheckArg;
038    import org.jboss.dna.graph.GraphI18n;
039    import org.jboss.dna.graph.properties.Binary;
040    import org.jboss.dna.graph.properties.DateTime;
041    import org.jboss.dna.graph.properties.IoException;
042    import org.jboss.dna.graph.properties.Name;
043    import org.jboss.dna.graph.properties.Path;
044    import org.jboss.dna.graph.properties.PathFactory;
045    import org.jboss.dna.graph.properties.PropertyType;
046    import org.jboss.dna.graph.properties.Reference;
047    import org.jboss.dna.graph.properties.ValueFactory;
048    import org.jboss.dna.graph.properties.ValueFormatException;
049    import org.jboss.dna.graph.properties.Path.Segment;
050    
051    /**
052     * The standard {@link ValueFactory} for {@link PropertyType#NAME} values.
053     * 
054     * @author Randall Hauch
055     * @author John Verhaeg
056     */
057    @Immutable
058    public class PathValueFactory extends AbstractValueFactory<Path> implements PathFactory {
059    
060        /**
061         * Regular expression used to identify the different segments in a path, using the standard '/' delimiter. The expression is
062         * simply:
063         * 
064         * <pre>
065         * /
066         * </pre>
067         */
068        protected static final Pattern DELIMITER_PATTERN = Pattern.compile("/");
069    
070        /**
071         * Regular expression used to identify the different parts of a segment. The expression is
072         * 
073         * <pre>
074         * ([&circ;*:/\[\]|]+)(:([&circ;*:/\[\]|]+))?(\[(\d+)])?
075         * </pre>
076         * 
077         * where the first part is accessed with group 1, the second part is accessed with group 3, and the index is accessed with
078         * group 5.
079         */
080        protected static final Pattern SEGMENT_PATTERN = Pattern.compile("([^:/]+)(:([^/\\[\\]]+))?(\\[(\\d+)])?");
081    
082        private final ValueFactory<Name> nameValueFactory;
083    
084        public PathValueFactory( TextDecoder decoder,
085                                 ValueFactory<String> stringValueFactory,
086                                 ValueFactory<Name> nameValueFactory ) {
087            super(PropertyType.PATH, decoder, stringValueFactory);
088            CheckArg.isNotNull(nameValueFactory, "nameValueFactory");
089            this.nameValueFactory = nameValueFactory;
090        }
091    
092        /**
093         * @return nameValueFactory
094         */
095        protected ValueFactory<Name> getNameValueFactory() {
096            return this.nameValueFactory;
097        }
098    
099        /**
100         * <p>
101         * {@inheritDoc}
102         * </p>
103         * 
104         * @see org.jboss.dna.graph.properties.PathFactory#createRootPath()
105         */
106        public Path createRootPath() {
107            return BasicPath.ROOT;
108        }
109    
110        /**
111         * {@inheritDoc}
112         */
113        public Path create( String value ) {
114            return create(value, getDecoder());
115        }
116    
117        /**
118         * {@inheritDoc}
119         */
120        public Path create( final String value,
121                            TextDecoder decoder ) {
122            if (value == null) return null;
123            String trimmedValue = value.trim();
124            int length = trimmedValue.length();
125            boolean absolute = false;
126            if (length == 0) {
127                return BasicPath.ROOT;
128            }
129    
130            // Remove the leading delimiter ...
131            if (trimmedValue.charAt(0) == Path.DELIMITER) {
132                trimmedValue = length > 1 ? trimmedValue.substring(1) : "";
133                --length;
134                absolute = true;
135            }
136            // remove the trailing delimiter ...
137            if (length > 0 && trimmedValue.charAt(length - 1) == Path.DELIMITER) {
138                trimmedValue = length > 1 ? trimmedValue.substring(0, length - 1) : "";
139                length = trimmedValue.length();
140            }
141            if (length == 0) {
142                return BasicPath.ROOT;
143            }
144    
145            // Parse the path into its segments ...
146            List<Segment> segments = new ArrayList<Segment>();
147            String[] pathSegments = DELIMITER_PATTERN.split(trimmedValue);
148            if (pathSegments.length == 0) {
149                throw new ValueFormatException(value, getPropertyType(), GraphI18n.validPathMayNotContainEmptySegment.text(value));
150            }
151            if (decoder == null) decoder = getDecoder();
152            assert pathSegments.length != 0;
153            assert decoder != null;
154            for (String segment : pathSegments) {
155                assert segment != null;
156                segment = segment.trim();
157                if (segment.length() == 0) {
158                    throw new ValueFormatException(value, getPropertyType(), GraphI18n.validPathMayNotContainEmptySegment.text(value));
159                }
160                // Create the name and add a segment with it ...
161                segments.add(createSegment(segment, decoder));
162            }
163    
164            // Create a path constructed from the supplied segments ...
165            return new BasicPath(segments, absolute);
166        }
167    
168        /**
169         * {@inheritDoc}
170         */
171        public Path create( int value ) {
172            throw new ValueFormatException(value, getPropertyType(),
173                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
174                                                                              Integer.class.getSimpleName(),
175                                                                              value));
176        }
177    
178        /**
179         * {@inheritDoc}
180         */
181        public Path create( long value ) {
182            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
183                                                                                                        Long.class.getSimpleName(),
184                                                                                                        value));
185        }
186    
187        /**
188         * {@inheritDoc}
189         */
190        public Path create( boolean value ) {
191            throw new ValueFormatException(value, getPropertyType(),
192                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
193                                                                              Boolean.class.getSimpleName(),
194                                                                              value));
195        }
196    
197        /**
198         * {@inheritDoc}
199         */
200        public Path create( float value ) {
201            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
202                                                                                                        Float.class.getSimpleName(),
203                                                                                                        value));
204        }
205    
206        /**
207         * {@inheritDoc}
208         */
209        public Path create( double value ) {
210            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
211                                                                                                        Double.class.getSimpleName(),
212                                                                                                        value));
213        }
214    
215        /**
216         * {@inheritDoc}
217         */
218        public Path create( BigDecimal value ) {
219            throw new ValueFormatException(value, getPropertyType(),
220                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
221                                                                              BigDecimal.class.getSimpleName(),
222                                                                              value));
223        }
224    
225        /**
226         * {@inheritDoc}
227         */
228        public Path create( Calendar value ) {
229            throw new ValueFormatException(value, getPropertyType(),
230                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
231                                                                              Calendar.class.getSimpleName(),
232                                                                              value));
233        }
234    
235        /**
236         * {@inheritDoc}
237         */
238        public Path create( Date value ) {
239            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
240                                                                                                        Date.class.getSimpleName(),
241                                                                                                        value));
242        }
243    
244        /**
245         * {@inheritDoc}
246         * 
247         * @see org.jboss.dna.graph.properties.ValueFactory#create(org.jboss.dna.graph.properties.DateTime)
248         */
249        public Path create( DateTime value ) throws ValueFormatException {
250            throw new ValueFormatException(value, getPropertyType(),
251                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
252                                                                              DateTime.class.getSimpleName(),
253                                                                              value));
254        }
255    
256        /**
257         * {@inheritDoc}
258         */
259        public Path create( Name value ) {
260            if (value == null) return null;
261            try {
262                List<Path.Segment> segments = new ArrayList<Path.Segment>(1);
263                segments.add(new BasicPathSegment(value));
264                return new BasicPath(segments, true);
265            } catch (IllegalArgumentException e) {
266                throw new ValueFormatException(value, getPropertyType(), e);
267            }
268        }
269    
270        /**
271         * {@inheritDoc}
272         */
273        public Path create( Path value ) {
274            return value;
275        }
276    
277        /**
278         * {@inheritDoc}
279         */
280        public Path createAbsolutePath( Name... segmentNames ) {
281            if (segmentNames == null || segmentNames.length == 0) return BasicPath.ROOT;
282            List<Segment> segments = new ArrayList<Segment>(segmentNames.length);
283            for (Name segmentName : segmentNames) {
284                if (segmentName == null) {
285                    CheckArg.containsNoNulls(segmentNames, "segment names");
286                }
287                segments.add(new BasicPathSegment(segmentName));
288            }
289            return new BasicPath(segments, true);
290        }
291    
292        /**
293         * {@inheritDoc}
294         */
295        public Path createAbsolutePath( Segment... segments ) {
296            if (segments == null || segments.length == 0) return BasicPath.ROOT;
297            List<Segment> segmentsList = new ArrayList<Segment>(segments.length);
298            for (Segment segment : segments) {
299                if (segment == null) {
300                    CheckArg.containsNoNulls(segments, "segments");
301                }
302                segmentsList.add(segment);
303            }
304            return new BasicPath(segmentsList, true);
305        }
306    
307        /**
308         * {@inheritDoc}
309         * 
310         * @see org.jboss.dna.graph.properties.PathFactory#createAbsolutePath(java.lang.Iterable)
311         */
312        public Path createAbsolutePath( Iterable<Segment> segments ) {
313            List<Segment> segmentsList = new LinkedList<Segment>();
314            for (Segment segment : segments) {
315                if (segment == null) {
316                    CheckArg.containsNoNulls(segments, "segments");
317                }
318                segmentsList.add(segment);
319            }
320            if (segmentsList.isEmpty()) return BasicPath.ROOT;
321            return new BasicPath(segmentsList, true);
322        }
323    
324        /**
325         * <p>
326         * {@inheritDoc}
327         * </p>
328         * 
329         * @see org.jboss.dna.graph.properties.PathFactory#createRelativePath()
330         */
331        public Path createRelativePath() {
332            return BasicPath.EMPTY_RELATIVE;
333        }
334    
335        /**
336         * {@inheritDoc}
337         */
338        public Path createRelativePath( Name... segmentNames ) {
339            if (segmentNames == null || segmentNames.length == 0) return BasicPath.EMPTY_RELATIVE;
340            List<Segment> segments = new ArrayList<Segment>(segmentNames.length);
341            for (Name segmentName : segmentNames) {
342                if (segmentName == null) {
343                    CheckArg.containsNoNulls(segmentNames, "segment names");
344                }
345                segments.add(new BasicPathSegment(segmentName));
346            }
347            return new BasicPath(segments, false);
348        }
349    
350        /**
351         * {@inheritDoc}
352         */
353        public Path createRelativePath( Segment... segments ) {
354            if (segments == null || segments.length == 0) return BasicPath.EMPTY_RELATIVE;
355            List<Segment> segmentsList = new ArrayList<Segment>(segments.length);
356            for (Segment segment : segments) {
357                if (segment == null) {
358                    CheckArg.containsNoNulls(segments, "segments");
359                }
360                segmentsList.add(segment);
361            }
362            return new BasicPath(segmentsList, false);
363        }
364    
365        /**
366         * {@inheritDoc}
367         * 
368         * @see org.jboss.dna.graph.properties.PathFactory#createRelativePath(java.lang.Iterable)
369         */
370        public Path createRelativePath( Iterable<Segment> segments ) {
371            List<Segment> segmentsList = new LinkedList<Segment>();
372            for (Segment segment : segments) {
373                if (segment == null) {
374                    CheckArg.containsNoNulls(segments, "segments");
375                }
376                segmentsList.add(segment);
377            }
378            if (segmentsList.isEmpty()) return BasicPath.EMPTY_RELATIVE;
379            return new BasicPath(segmentsList, false);
380        }
381    
382        /**
383         * {@inheritDoc}
384         * 
385         * @see org.jboss.dna.graph.properties.PathFactory#create(org.jboss.dna.graph.properties.Path,
386         *      org.jboss.dna.graph.properties.Path)
387         */
388        public Path create( Path parentPath,
389                            Path childPath ) {
390            CheckArg.isNotNull(parentPath, "parent path");
391            CheckArg.isNotNull(childPath, "child path");
392            if (childPath.size() == 0) return parentPath;
393            if (parentPath.size() == 0) {
394                // Just need to return the child path, but it must be absolute if the parent is ...
395                if (childPath.isAbsolute() == parentPath.isAbsolute()) return childPath;
396                // They aren't the same absoluteness, so create a new one ...
397                return new BasicPath(childPath.getSegmentsList(), parentPath.isAbsolute());
398            }
399            List<Segment> segments = new ArrayList<Segment>(parentPath.size() + childPath.size());
400            segments.addAll(parentPath.getSegmentsList());
401            segments.addAll(childPath.getSegmentsList());
402            return new BasicPath(segments, parentPath.isAbsolute());
403        }
404    
405        /**
406         * {@inheritDoc}
407         */
408        public Path create( Path parentPath,
409                            Name segmentName,
410                            int index ) {
411            CheckArg.isNotNull(parentPath, "parent path");
412            CheckArg.isNotNull(segmentName, "segment name");
413            List<Segment> segments = new ArrayList<Segment>(parentPath.size() + 1);
414            segments.addAll(parentPath.getSegmentsList());
415            segments.add(new BasicPathSegment(segmentName, index));
416            return new BasicPath(segments, parentPath.isAbsolute());
417        }
418    
419        /**
420         * {@inheritDoc}
421         */
422        public Path create( Path parentPath,
423                            Name... segmentNames ) {
424            CheckArg.isNotNull(parentPath, "parent path");
425            if (segmentNames == null || segmentNames.length == 0) return parentPath;
426    
427            List<Segment> segments = new ArrayList<Segment>(parentPath.size() + 1);
428            segments.addAll(parentPath.getSegmentsList());
429            for (Name segmentName : segmentNames) {
430                if (segmentName == null) {
431                    CheckArg.containsNoNulls(segmentNames, "segment names");
432                }
433                segments.add(new BasicPathSegment(segmentName));
434            }
435            return new BasicPath(segments, parentPath.isAbsolute());
436        }
437    
438        /**
439         * {@inheritDoc}
440         */
441        public Path create( Path parentPath,
442                            Segment... segments ) {
443            CheckArg.isNotNull(parentPath, "parent path");
444            if (segments == null || segments.length == 0) return BasicPath.ROOT;
445    
446            List<Segment> segmentsList = new ArrayList<Segment>(parentPath.size() + 1);
447            segmentsList.addAll(parentPath.getSegmentsList());
448            for (Segment segment : segments) {
449                if (segment == null) {
450                    CheckArg.containsNoNulls(segments, "segments");
451                }
452                segmentsList.add(segment);
453            }
454            return new BasicPath(segmentsList, parentPath.isAbsolute());
455        }
456    
457        /**
458         * {@inheritDoc}
459         * 
460         * @see org.jboss.dna.graph.properties.PathFactory#create(org.jboss.dna.graph.properties.Path, java.lang.Iterable)
461         */
462        public Path create( Path parentPath,
463                            Iterable<Segment> segments ) {
464            CheckArg.isNotNull(parentPath, "parent path");
465    
466            List<Segment> segmentsList = new LinkedList<Segment>();
467            segmentsList.addAll(parentPath.getSegmentsList());
468            for (Segment segment : segments) {
469                if (segment == null) {
470                    CheckArg.containsNoNulls(segments, "segments");
471                }
472                segmentsList.add(segment);
473            }
474            if (segmentsList.isEmpty()) return BasicPath.ROOT;
475            return new BasicPath(segmentsList, parentPath.isAbsolute());
476        }
477    
478        /**
479         * {@inheritDoc}
480         * 
481         * @see org.jboss.dna.graph.properties.PathFactory#create(org.jboss.dna.graph.properties.Path, java.lang.String)
482         */
483        public Path create( Path parentPath,
484                            String subpath ) {
485            // Create a relative path for the subpath ...
486            Path relativeSubpath = create(subpath);
487            return create(parentPath, relativeSubpath);
488        }
489    
490        /**
491         * {@inheritDoc}
492         */
493        public Segment createSegment( Name segmentName ) {
494            CheckArg.isNotNull(segmentName, "segment name");
495            if (Path.SELF_NAME.equals(segmentName)) return Path.SELF_SEGMENT;
496            if (Path.PARENT_NAME.equals(segmentName)) return Path.PARENT_SEGMENT;
497            return new BasicPathSegment(segmentName);
498        }
499    
500        /**
501         * {@inheritDoc}
502         */
503        public Segment createSegment( Name segmentName,
504                                      int index ) {
505            CheckArg.isNotNull(segmentName, "segment name");
506            if (Path.SELF_NAME.equals(segmentName)) return Path.SELF_SEGMENT;
507            if (Path.PARENT_NAME.equals(segmentName)) return Path.PARENT_SEGMENT;
508            return new BasicPathSegment(segmentName, index);
509        }
510    
511        /**
512         * {@inheritDoc}
513         * 
514         * @see org.jboss.dna.graph.properties.PathFactory#createSegment(java.lang.String)
515         */
516        public Segment createSegment( String segmentName ) {
517            return createSegment(segmentName, getDecoder());
518        }
519    
520        /**
521         * <p>
522         * {@inheritDoc}
523         * </p>
524         * 
525         * @see org.jboss.dna.graph.properties.PathFactory#createSegment(java.lang.String, org.jboss.dna.common.text.TextDecoder)
526         */
527        public Segment createSegment( String segmentName,
528                                      TextDecoder decoder ) {
529            CheckArg.isNotNull(segmentName, "segment name");
530            if (Path.SELF.equals(segmentName)) return Path.SELF_SEGMENT;
531            if (Path.PARENT.equals(segmentName)) return Path.PARENT_SEGMENT;
532            int startBracketNdx = segmentName.indexOf('[');
533            if (startBracketNdx < 0) {
534                return new BasicPathSegment(this.nameValueFactory.create(segmentName, decoder));
535            }
536            int endBracketNdx = segmentName.indexOf(']', startBracketNdx);
537            if (endBracketNdx < 0) {
538                throw new IllegalArgumentException(GraphI18n.missingEndBracketInSegmentName.text(segmentName));
539            }
540            String ndx = segmentName.substring(startBracketNdx + 1, endBracketNdx);
541            try {
542                return new BasicPathSegment(this.nameValueFactory.create(segmentName.substring(0, startBracketNdx), decoder),
543                                            Integer.parseInt(ndx));
544            } catch (NumberFormatException err) {
545                throw new ValueFormatException(segmentName, getPropertyType(), GraphI18n.invalidIndexInSegmentName.text(ndx,
546                                                                                                                        segmentName));
547            }
548        }
549    
550        /**
551         * {@inheritDoc}
552         */
553        public Segment createSegment( String segmentName,
554                                      int index ) {
555            CheckArg.isNotNull(segmentName, "segment name");
556            if (Path.SELF.equals(segmentName)) return Path.SELF_SEGMENT;
557            if (Path.PARENT.equals(segmentName)) return Path.PARENT_SEGMENT;
558            return new BasicPathSegment(this.nameValueFactory.create(segmentName), index);
559        }
560    
561        /**
562         * {@inheritDoc}
563         */
564        public Path create( Reference value ) {
565            throw new ValueFormatException(value, getPropertyType(),
566                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
567                                                                              Reference.class.getSimpleName(),
568                                                                              value));
569        }
570    
571        /**
572         * {@inheritDoc}
573         */
574        public Path create( URI value ) {
575            if (value == null) return null;
576            String asciiString = value.toASCIIString();
577            // Remove any leading "./" ...
578            if (asciiString.startsWith("./") && asciiString.length() > 2) {
579                asciiString = asciiString.substring(2);
580            }
581            if (asciiString.indexOf('/') == -1) {
582                return create(asciiString);
583            }
584            throw new ValueFormatException(value, getPropertyType(), GraphI18n.errorConvertingType.text(URI.class.getSimpleName(),
585                                                                                                        Path.class.getSimpleName(),
586                                                                                                        value));
587        }
588    
589        /**
590         * {@inheritDoc}
591         * 
592         * @see org.jboss.dna.graph.properties.ValueFactory#create(java.util.UUID)
593         */
594        public Path create( UUID value ) {
595            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
596                                                                                                        UUID.class.getSimpleName(),
597                                                                                                        value));
598        }
599    
600        /**
601         * {@inheritDoc}
602         */
603        public Path create( byte[] value ) {
604            // First attempt to create a string from the value, then a long from the string ...
605            return create(getStringValueFactory().create(value));
606        }
607    
608        /**
609         * {@inheritDoc}
610         * 
611         * @see org.jboss.dna.graph.properties.ValueFactory#create(org.jboss.dna.graph.properties.Binary)
612         */
613        public Path create( Binary value ) throws ValueFormatException, IoException {
614            // First create a string and then create the boolean from the string value ...
615            return create(getStringValueFactory().create(value));
616        }
617    
618        /**
619         * {@inheritDoc}
620         */
621        public Path create( InputStream stream,
622                            long approximateLength ) throws IoException {
623            // First attempt to create a string from the value, then a double from the string ...
624            return create(getStringValueFactory().create(stream, approximateLength));
625        }
626    
627        /**
628         * {@inheritDoc}
629         */
630        public Path create( Reader reader,
631                            long approximateLength ) throws IoException {
632            // First attempt to create a string from the value, then a double from the string ...
633            return create(getStringValueFactory().create(reader, approximateLength));
634        }
635    
636        /**
637         * {@inheritDoc}
638         */
639        @Override
640        protected Path[] createEmptyArray( int length ) {
641            return new Path[length];
642        }
643    
644    }