001/* 002 * Copyright 2011-2016 UnboundID Corp. 003 * 004 * This program is free software; you can redistribute it and/or modify 005 * it under the terms of the GNU General Public License (GPLv2 only) 006 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 007 * as published by the Free Software Foundation. 008 * 009 * This program is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU General Public License for more details. 013 * 014 * You should have received a copy of the GNU General Public License 015 * along with this program; if not, see <http://www.gnu.org/licenses>. 016 */ 017 018package com.unboundid.scim.sdk; 019 020import com.unboundid.scim.schema.CoreSchema; 021 022import java.util.regex.Matcher; 023import java.util.regex.Pattern; 024 025 026 027/** 028 * This class represents a path to an attribute or sub-attribute. The path is 029 * a string comprising an schema URI (the SCIM core schema is assumed 030 * if absent), an attribute name, and an optional sub-attribute name. 031 */ 032public class AttributePath 033{ 034 /** 035 * A regular expression to match the components of an attribute path. 036 * Given the path "urn:scim:schemas:core:user:1.0:name.familyName" then 037 * group 2 will match "urn:scim:schemas:core:user:1.0", group 3 matches 038 * "name" and group 5 matches "familyName". 039 */ 040 private static final Pattern pattern = 041 Pattern.compile("^((.+):)?([^.]+)(\\.(.+))?$"); 042 043 /** 044 * The URI of the attribute schema. 045 */ 046 private final String attributeSchema; 047 048 /** 049 * The name of the attribute. 050 */ 051 private final String attributeName; 052 053 /** 054 * The name of the sub-attribute, or {@code null} if absent. 055 */ 056 private final String subAttributeName; 057 058 059 060 /** 061 * Create a new attribute path. 062 * 063 * @param attributeSchema The URI of the attribute schema. 064 * @param attributeName The name of the attribute. 065 * @param subAttributeName The name of the sub-attribute, or {@code null} if 066 * absent. 067 */ 068 public AttributePath(final String attributeSchema, 069 final String attributeName, 070 final String subAttributeName) 071 { 072 this.attributeSchema = attributeSchema; 073 this.attributeName = attributeName; 074 this.subAttributeName = subAttributeName; 075 } 076 077 078 079 /** 080 * Parse an attribute path. 081 * 082 * @param path The attribute path. 083 * 084 * @return The parsed attribute path. 085 */ 086 public static AttributePath parse(final String path) 087 { 088 final Matcher matcher = pattern.matcher(path); 089 090 if (!matcher.matches() || matcher.groupCount() != 5) 091 { 092 throw new IllegalArgumentException( 093 String.format( 094 "'%s' does not match '[schema:]attr[.sub-attr]' format", path)); 095 } 096 097 final String attributeSchema = matcher.group(2); 098 final String attributeName = matcher.group(3); 099 final String subAttributeName = matcher.group(5); 100 101 if (attributeSchema != null) 102 { 103 return new AttributePath(attributeSchema, attributeName, 104 subAttributeName); 105 } 106 else 107 { 108 return new AttributePath(SCIMConstants.SCHEMA_URI_CORE, attributeName, 109 subAttributeName); 110 } 111 } 112 113 114 115 /** 116 * Parse an attribute path. 117 * 118 * @param path The attribute path. 119 * @param defaultSchema The default schema to assume for attributes that do 120 * not have the schema part of the urn specified. The 121 * 'id', 'externalId', and 'meta' attributes will always 122 * assume the SCIM Core schema. 123 * 124 * @return The parsed attribute path. 125 */ 126 public static AttributePath parse(final String path, 127 final String defaultSchema) 128 { 129 final Matcher matcher = pattern.matcher(path); 130 131 if (!matcher.matches() || matcher.groupCount() != 5) 132 { 133 throw new IllegalArgumentException( 134 String.format( 135 "'%s' does not match '[schema:]attr[.sub-attr]' format", path)); 136 } 137 138 final String attributeSchema = matcher.group(2); 139 final String attributeName = matcher.group(3); 140 final String subAttributeName = matcher.group(5); 141 142 if (attributeSchema != null) 143 { 144 return new AttributePath(attributeSchema, attributeName, 145 subAttributeName); 146 } 147 else 148 { 149 if (attributeName.equalsIgnoreCase( 150 CoreSchema.ID_DESCRIPTOR.getName()) || 151 attributeName.equalsIgnoreCase( 152 CoreSchema.EXTERNAL_ID_DESCRIPTOR.getName()) || 153 attributeName.equalsIgnoreCase( 154 CoreSchema.META_DESCRIPTOR.getName())) 155 { 156 return new AttributePath(SCIMConstants.SCHEMA_URI_CORE, attributeName, 157 subAttributeName); 158 } 159 else 160 { 161 return new AttributePath(defaultSchema, attributeName, 162 subAttributeName); 163 } 164 } 165 } 166 167 168 169 /** 170 * {@inheritDoc} 171 */ 172 @Override 173 public String toString() 174 { 175 final StringBuilder builder = new StringBuilder(); 176 toString(builder); 177 return builder.toString(); 178 } 179 180 181 182 /** 183 * Append the string representation of the attribute path to the provided 184 * buffer. 185 * 186 * @param builder The buffer to which the string representation of the 187 * attribute path is to be appended. 188 */ 189 public void toString(final StringBuilder builder) 190 { 191 if (!attributeSchema.equalsIgnoreCase(SCIMConstants.SCHEMA_URI_CORE)) 192 { 193 builder.append(attributeSchema); 194 builder.append(':'); 195 } 196 197 builder.append(attributeName); 198 if (subAttributeName != null) 199 { 200 builder.append('.'); 201 builder.append(subAttributeName); 202 } 203 } 204 205 206 207 /** 208 * Retrieve the URI of the attribute schema. 209 * @return The URI of the attribute schema. 210 */ 211 public String getAttributeSchema() 212 { 213 return attributeSchema; 214 } 215 216 217 218 /** 219 * Retrieve the name of the attribute. 220 * @return The name of the attribute. 221 */ 222 public String getAttributeName() 223 { 224 return attributeName; 225 } 226 227 228 229 /** 230 * Retrieve the name of the sub-attribute, or {@code null} if absent. 231 * @return The name of the sub-attribute, or {@code null} if absent. 232 */ 233 public String getSubAttributeName() 234 { 235 return subAttributeName; 236 } 237}