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 }