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 * ([ˆ*:/\[\]|]+)(:([ˆ*:/\[\]|]+))?(\[(\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 }