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(),
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 }