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(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
173                                                                                                      Integer.class.getSimpleName(),
174                                                                                                      value));
175        }
176    
177        /**
178         * {@inheritDoc}
179         */
180        public Path create( long value ) {
181            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
182                                                                                                      Long.class.getSimpleName(),
183                                                                                                      value));
184        }
185    
186        /**
187         * {@inheritDoc}
188         */
189        public Path create( boolean value ) {
190            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
191                                                                                                      Boolean.class.getSimpleName(),
192                                                                                                      value));
193        }
194    
195        /**
196         * {@inheritDoc}
197         */
198        public Path create( float value ) {
199            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
200                                                                                                      Float.class.getSimpleName(),
201                                                                                                      value));
202        }
203    
204        /**
205         * {@inheritDoc}
206         */
207        public Path create( double value ) {
208            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
209                                                                                                      Double.class.getSimpleName(),
210                                                                                                      value));
211        }
212    
213        /**
214         * {@inheritDoc}
215         */
216        public Path create( BigDecimal value ) {
217            throw new ValueFormatException(value, getPropertyType(),
218                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
219                                                                            BigDecimal.class.getSimpleName(),
220                                                                            value));
221        }
222    
223        /**
224         * {@inheritDoc}
225         */
226        public Path create( Calendar value ) {
227            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
228                                                                                                      Calendar.class.getSimpleName(),
229                                                                                                      value));
230        }
231    
232        /**
233         * {@inheritDoc}
234         */
235        public Path create( Date value ) {
236            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
237                                                                                                      Date.class.getSimpleName(),
238                                                                                                      value));
239        }
240    
241        /**
242         * {@inheritDoc}
243         * 
244         * @see org.jboss.dna.graph.properties.ValueFactory#create(org.jboss.dna.graph.properties.DateTime)
245         */
246        public Path create( DateTime value ) throws ValueFormatException {
247            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
248                                                                                                      DateTime.class.getSimpleName(),
249                                                                                                      value));
250        }
251    
252        /**
253         * {@inheritDoc}
254         */
255        public Path create( Name value ) {
256            if (value == null) return null;
257            try {
258                List<Path.Segment> segments = new ArrayList<Path.Segment>(1);
259                segments.add(new BasicPathSegment(value));
260                return new BasicPath(segments, true);
261            } catch (IllegalArgumentException e) {
262                throw new ValueFormatException(value, getPropertyType(), e);
263            }
264        }
265    
266        /**
267         * {@inheritDoc}
268         */
269        public Path create( Path value ) {
270            return value;
271        }
272    
273        /**
274         * {@inheritDoc}
275         */
276        public Path createAbsolutePath( Name... segmentNames ) {
277            if (segmentNames == null || segmentNames.length == 0) return BasicPath.ROOT;
278            List<Segment> segments = new ArrayList<Segment>(segmentNames.length);
279            for (Name segmentName : segmentNames) {
280                if (segmentName == null) {
281                    CheckArg.containsNoNulls(segmentNames, "segment names");
282                }
283                segments.add(new BasicPathSegment(segmentName));
284            }
285            return new BasicPath(segments, true);
286        }
287    
288        /**
289         * {@inheritDoc}
290         */
291        public Path createAbsolutePath( Segment... segments ) {
292            if (segments == null || segments.length == 0) return BasicPath.ROOT;
293            List<Segment> segmentsList = new ArrayList<Segment>(segments.length);
294            for (Segment segment : segments) {
295                if (segment == null) {
296                    CheckArg.containsNoNulls(segments, "segments");
297                }
298                segmentsList.add(segment);
299            }
300            return new BasicPath(segmentsList, true);
301        }
302    
303        /**
304         * {@inheritDoc}
305         * 
306         * @see org.jboss.dna.graph.properties.PathFactory#createAbsolutePath(java.lang.Iterable)
307         */
308        public Path createAbsolutePath( Iterable<Segment> segments ) {
309            List<Segment> segmentsList = new LinkedList<Segment>();
310            for (Segment segment : segments) {
311                if (segment == null) {
312                    CheckArg.containsNoNulls(segments, "segments");
313                }
314                segmentsList.add(segment);
315            }
316            if (segmentsList.isEmpty()) return BasicPath.ROOT;
317            return new BasicPath(segmentsList, true);
318        }
319    
320        /**
321         * <p>
322         * {@inheritDoc}
323         * </p>
324         * 
325         * @see org.jboss.dna.graph.properties.PathFactory#createRelativePath()
326         */
327        public Path createRelativePath() {
328            return BasicPath.SELF_PATH;
329        }
330    
331        /**
332         * {@inheritDoc}
333         */
334        public Path createRelativePath( Name... segmentNames ) {
335            if (segmentNames == null || segmentNames.length == 0) return BasicPath.SELF_PATH;
336            List<Segment> segments = new ArrayList<Segment>(segmentNames.length);
337            for (Name segmentName : segmentNames) {
338                if (segmentName == null) {
339                    CheckArg.containsNoNulls(segmentNames, "segment names");
340                }
341                segments.add(new BasicPathSegment(segmentName));
342            }
343            return new BasicPath(segments, false);
344        }
345    
346        /**
347         * {@inheritDoc}
348         */
349        public Path createRelativePath( Segment... segments ) {
350            if (segments == null || segments.length == 0) return BasicPath.SELF_PATH;
351            List<Segment> segmentsList = new ArrayList<Segment>(segments.length);
352            for (Segment segment : segments) {
353                if (segment == null) {
354                    CheckArg.containsNoNulls(segments, "segments");
355                }
356                segmentsList.add(segment);
357            }
358            return new BasicPath(segmentsList, false);
359        }
360    
361        /**
362         * {@inheritDoc}
363         * 
364         * @see org.jboss.dna.graph.properties.PathFactory#createRelativePath(java.lang.Iterable)
365         */
366        public Path createRelativePath( Iterable<Segment> segments ) {
367            List<Segment> segmentsList = new LinkedList<Segment>();
368            for (Segment segment : segments) {
369                if (segment == null) {
370                    CheckArg.containsNoNulls(segments, "segments");
371                }
372                segmentsList.add(segment);
373            }
374            if (segmentsList.isEmpty()) return BasicPath.SELF_PATH;
375            return new BasicPath(segmentsList, false);
376        }
377    
378        /**
379         * {@inheritDoc}
380         * 
381         * @see org.jboss.dna.graph.properties.PathFactory#create(org.jboss.dna.graph.properties.Path, org.jboss.dna.graph.properties.Path)
382         */
383        public Path create( Path parentPath,
384                            Path childPath ) {
385            CheckArg.isNotNull(parentPath, "parent path");
386            CheckArg.isNotNull(childPath, "child path");
387            if (childPath.size() == 0) return parentPath;
388            if (parentPath.size() == 0) {
389                // Just need to return the child path, but it must be absolute if the parent is ...
390                if (childPath.isAbsolute() == parentPath.isAbsolute()) return childPath;
391                // They aren't the same absoluteness, so create a new one ...
392                return new BasicPath(childPath.getSegmentsList(), parentPath.isAbsolute());
393            }
394            List<Segment> segments = new ArrayList<Segment>(parentPath.size() + childPath.size());
395            segments.addAll(parentPath.getSegmentsList());
396            segments.addAll(childPath.getSegmentsList());
397            return new BasicPath(segments, parentPath.isAbsolute());
398        }
399    
400        /**
401         * {@inheritDoc}
402         */
403        public Path create( Path parentPath,
404                            Name segmentName,
405                            int index ) {
406            CheckArg.isNotNull(parentPath, "parent path");
407            CheckArg.isNotNull(segmentName, "segment name");
408            List<Segment> segments = new ArrayList<Segment>(parentPath.size() + 1);
409            segments.addAll(parentPath.getSegmentsList());
410            segments.add(new BasicPathSegment(segmentName, index));
411            return new BasicPath(segments, parentPath.isAbsolute());
412        }
413    
414        /**
415         * {@inheritDoc}
416         */
417        public Path create( Path parentPath,
418                            Name... segmentNames ) {
419            CheckArg.isNotNull(parentPath, "parent path");
420            if (segmentNames == null || segmentNames.length == 0) return parentPath;
421    
422            List<Segment> segments = new ArrayList<Segment>(parentPath.size() + 1);
423            segments.addAll(parentPath.getSegmentsList());
424            for (Name segmentName : segmentNames) {
425                if (segmentName == null) {
426                    CheckArg.containsNoNulls(segmentNames, "segment names");
427                }
428                segments.add(new BasicPathSegment(segmentName));
429            }
430            return new BasicPath(segments, parentPath.isAbsolute());
431        }
432    
433        /**
434         * {@inheritDoc}
435         */
436        public Path create( Path parentPath,
437                            Segment... segments ) {
438            CheckArg.isNotNull(parentPath, "parent path");
439            if (segments == null || segments.length == 0) return BasicPath.ROOT;
440    
441            List<Segment> segmentsList = new ArrayList<Segment>(parentPath.size() + 1);
442            segmentsList.addAll(parentPath.getSegmentsList());
443            for (Segment segment : segments) {
444                if (segment == null) {
445                    CheckArg.containsNoNulls(segments, "segments");
446                }
447                segmentsList.add(segment);
448            }
449            return new BasicPath(segmentsList, parentPath.isAbsolute());
450        }
451    
452        /**
453         * {@inheritDoc}
454         * 
455         * @see org.jboss.dna.graph.properties.PathFactory#create(org.jboss.dna.graph.properties.Path, java.lang.Iterable)
456         */
457        public Path create( Path parentPath,
458                            Iterable<Segment> segments ) {
459            CheckArg.isNotNull(parentPath, "parent path");
460    
461            List<Segment> segmentsList = new LinkedList<Segment>();
462            segmentsList.addAll(parentPath.getSegmentsList());
463            for (Segment segment : segments) {
464                if (segment == null) {
465                    CheckArg.containsNoNulls(segments, "segments");
466                }
467                segmentsList.add(segment);
468            }
469            if (segmentsList.isEmpty()) return BasicPath.ROOT;
470            return new BasicPath(segmentsList, parentPath.isAbsolute());
471        }
472    
473        /**
474         * {@inheritDoc}
475         * 
476         * @see org.jboss.dna.graph.properties.PathFactory#create(org.jboss.dna.graph.properties.Path, java.lang.String)
477         */
478        public Path create( Path parentPath,
479                            String subpath ) {
480            // Create a relative path for the subpath ...
481            Path relativeSubpath = create(subpath);
482            return create(parentPath, relativeSubpath);
483        }
484    
485        /**
486         * {@inheritDoc}
487         */
488        public Segment createSegment( Name segmentName ) {
489            CheckArg.isNotNull(segmentName, "segment name");
490            if (Path.SELF_NAME.equals(segmentName)) return Path.SELF_SEGMENT;
491            if (Path.PARENT_NAME.equals(segmentName)) return Path.PARENT_SEGMENT;
492            return new BasicPathSegment(segmentName);
493        }
494    
495        /**
496         * {@inheritDoc}
497         */
498        public Segment createSegment( Name segmentName,
499                                      int index ) {
500            CheckArg.isNotNull(segmentName, "segment name");
501            if (Path.SELF_NAME.equals(segmentName)) return Path.SELF_SEGMENT;
502            if (Path.PARENT_NAME.equals(segmentName)) return Path.PARENT_SEGMENT;
503            return new BasicPathSegment(segmentName, index);
504        }
505    
506        /**
507         * {@inheritDoc}
508         * 
509         * @see org.jboss.dna.graph.properties.PathFactory#createSegment(java.lang.String)
510         */
511        public Segment createSegment( String segmentName ) {
512            return createSegment(segmentName, getDecoder());
513        }
514    
515        /**
516         * <p>
517         * {@inheritDoc}
518         * </p>
519         * 
520         * @see org.jboss.dna.graph.properties.PathFactory#createSegment(java.lang.String, org.jboss.dna.common.text.TextDecoder)
521         */
522        public Segment createSegment( String segmentName,
523                                      TextDecoder decoder ) {
524            CheckArg.isNotNull(segmentName, "segment name");
525            if (Path.SELF.equals(segmentName)) return Path.SELF_SEGMENT;
526            if (Path.PARENT.equals(segmentName)) return Path.PARENT_SEGMENT;
527            int startBracketNdx = segmentName.indexOf('[');
528            if (startBracketNdx < 0) {
529                return new BasicPathSegment(this.nameValueFactory.create(segmentName, decoder));
530            }
531            int endBracketNdx = segmentName.indexOf(']', startBracketNdx);
532            if (endBracketNdx < 0) {
533                throw new IllegalArgumentException(GraphI18n.missingEndBracketInSegmentName.text(segmentName));
534            }
535            String ndx = segmentName.substring(startBracketNdx + 1, endBracketNdx);
536            try {
537                return new BasicPathSegment(this.nameValueFactory.create(segmentName.substring(0, startBracketNdx), decoder),
538                                            Integer.parseInt(ndx));
539            } catch (NumberFormatException err) {
540                throw new ValueFormatException(segmentName, getPropertyType(), GraphI18n.invalidIndexInSegmentName.text(ndx,
541                                                                                                                      segmentName));
542            }
543        }
544    
545        /**
546         * {@inheritDoc}
547         */
548        public Segment createSegment( String segmentName,
549                                      int index ) {
550            CheckArg.isNotNull(segmentName, "segment name");
551            if (Path.SELF.equals(segmentName)) return Path.SELF_SEGMENT;
552            if (Path.PARENT.equals(segmentName)) return Path.PARENT_SEGMENT;
553            return new BasicPathSegment(this.nameValueFactory.create(segmentName), index);
554        }
555    
556        /**
557         * {@inheritDoc}
558         */
559        public Path create( Reference value ) {
560            throw new ValueFormatException(value, getPropertyType(),
561                                           GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
562                                                                            Reference.class.getSimpleName(),
563                                                                            value));
564        }
565    
566        /**
567         * {@inheritDoc}
568         */
569        public Path create( URI value ) {
570            if (value == null) return null;
571            String asciiString = value.toASCIIString();
572            // Remove any leading "./" ...
573            if (asciiString.startsWith("./") && asciiString.length() > 2) {
574                asciiString = asciiString.substring(2);
575            }
576            if (asciiString.indexOf('/') == -1) {
577                return create(asciiString);
578            }
579            throw new ValueFormatException(value, getPropertyType(), GraphI18n.errorConvertingType.text(URI.class.getSimpleName(),
580                                                                                                      Path.class.getSimpleName(),
581                                                                                                      value));
582        }
583    
584        /**
585         * {@inheritDoc}
586         * 
587         * @see org.jboss.dna.graph.properties.ValueFactory#create(java.util.UUID)
588         */
589        public Path create( UUID value ) {
590            throw new ValueFormatException(value, getPropertyType(), GraphI18n.unableToCreateValue.text(getPropertyType().getName(),
591                                                                                                      UUID.class.getSimpleName(),
592                                                                                                      value));
593        }
594    
595        /**
596         * {@inheritDoc}
597         */
598        public Path create( byte[] value ) {
599            // First attempt to create a string from the value, then a long from the string ...
600            return create(getStringValueFactory().create(value));
601        }
602    
603        /**
604         * {@inheritDoc}
605         * 
606         * @see org.jboss.dna.graph.properties.ValueFactory#create(org.jboss.dna.graph.properties.Binary)
607         */
608        public Path create( Binary value ) throws ValueFormatException, IoException {
609            // First create a string and then create the boolean from the string value ...
610            return create(getStringValueFactory().create(value));
611        }
612    
613        /**
614         * {@inheritDoc}
615         */
616        public Path create( InputStream stream,
617                            long approximateLength ) throws IoException {
618            // First attempt to create a string from the value, then a double from the string ...
619            return create(getStringValueFactory().create(stream, approximateLength));
620        }
621    
622        /**
623         * {@inheritDoc}
624         */
625        public Path create( Reader reader,
626                            long approximateLength ) throws IoException {
627            // First attempt to create a string from the value, then a double from the string ...
628            return create(getStringValueFactory().create(reader, approximateLength));
629        }
630    
631        /**
632         * {@inheritDoc}
633         */
634        @Override
635        protected Path[] createEmptyArray( int length ) {
636            return new Path[length];
637        }
638    
639    }