001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.api.ldap.model.entry; 021 022 023import org.apache.directory.api.i18n.I18n; 024import org.apache.directory.api.ldap.model.exception.LdapException; 025import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException; 026import org.apache.directory.api.ldap.model.message.ResultCodeEnum; 027import org.apache.directory.api.ldap.model.schema.AttributeType; 028import org.apache.directory.api.ldap.model.schema.LdapComparator; 029import org.apache.directory.api.ldap.model.schema.LdapSyntax; 030import org.apache.directory.api.ldap.model.schema.MatchingRule; 031import org.apache.directory.api.ldap.model.schema.Normalizer; 032import org.apache.directory.api.ldap.model.schema.SyntaxChecker; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036 037/** 038 * A wrapper around byte[] values in entries. 039 * 040 * @param <T> The valye type 041 * 042 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 043 */ 044public abstract class AbstractValue<T> implements Value<T> 045{ 046 /** logger for reporting errors that might not be handled properly upstream */ 047 protected static final Logger LOG = LoggerFactory.getLogger( AbstractValue.class ); 048 049 /** reference to the attributeType zssociated with the value */ 050 protected transient AttributeType attributeType; 051 052 /** the User Provided value */ 053 protected T upValue; 054 055 /** the canonical representation of the user provided value */ 056 protected T normalizedValue; 057 058 /** The computed hashcode. We don't want to compute it each time the hashcode() method is called */ 059 protected volatile int h; 060 061 062 /** 063 * {@inheritDoc} 064 */ 065 @SuppressWarnings("unchecked") 066 @Override 067 public Value<T> clone() 068 { 069 try 070 { 071 return ( Value<T> ) super.clone(); 072 } 073 catch ( CloneNotSupportedException cnse ) 074 { 075 // Do nothing 076 return null; 077 } 078 } 079 080 081 /** 082 * {@inheritDoc} 083 */ 084 @Override 085 public T getReference() 086 { 087 return upValue; 088 } 089 090 091 /** 092 * Get the wrapped value as a String. 093 * 094 * @return the wrapped value as a String 095 */ 096 @Override 097 public String getString() 098 { 099 throw new UnsupportedOperationException( "Cannot call this method on a binary value" ); 100 } 101 102 103 /** 104 * Get the wrapped value as a byte[]. 105 * 106 * @return the wrapped value as a byte[] 107 */ 108 @Override 109 public byte[] getBytes() 110 { 111 throw new UnsupportedOperationException( "Cannot call this method on a String value" ); 112 } 113 114 115 /** 116 * {@inheritDoc} 117 */ 118 @Override 119 public AttributeType getAttributeType() 120 { 121 return attributeType; 122 } 123 124 125 /** 126 * Apply an AttributeType to the current Value, normalizing it. 127 * 128 * @param attributeType The AttributeType to apply 129 * @throws LdapInvalidAttributeValueException If the value is not valid accordingly 130 * to the schema 131 */ 132 @SuppressWarnings("unchecked") 133 @Override 134 public void apply( AttributeType attributeType ) throws LdapInvalidAttributeValueException 135 { 136 if ( this.attributeType != null ) 137 { 138 // We already have applied an AttributeType, get out 139 LOG.warn( "AttributeType {0} already applied", attributeType.getName() ); 140 return; 141 } 142 143 if ( attributeType == null ) 144 { 145 // No attributeType : the normalized value and the user provided value are the same 146 normalizedValue = upValue; 147 return; 148 } 149 150 this.attributeType = attributeType; 151 152 // We first have to normalize the value before we can check its syntax 153 // Get the equality matchingRule, if we have one 154 MatchingRule equality = attributeType.getEquality(); 155 156 if ( equality != null ) 157 { 158 // If we have an Equality MR, we *must* have a normalizer 159 Normalizer normalizer = equality.getNormalizer(); 160 161 if ( normalizer != null ) 162 { 163 if ( upValue != null ) 164 { 165 boolean isHR = true; 166 // Some broken LDAP servers do not have proper syntax definitions 167 if ( attributeType.getSyntax() != null ) 168 { 169 isHR = attributeType.getSyntax().isHumanReadable(); 170 } 171 172 173 if ( isHR != isHumanReadable() ) 174 { 175 176 String message = "The '" + attributeType.getName() + "' AttributeType and values must " 177 + "both be String or binary"; 178 LOG.error( message ); 179 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message ); 180 } 181 182 try 183 { 184 if ( isHumanReadable() ) 185 { 186 if ( normalizedValue != null ) 187 { 188 normalizedValue = ( T ) normalizer.normalize( ( String ) normalizedValue ); 189 } 190 else 191 { 192 normalizedValue = ( T ) normalizer.normalize( ( String ) upValue ); 193 } 194 } 195 else 196 { 197 normalizedValue = ( T ) normalizer.normalize( new BinaryValue( ( byte[] ) upValue ) ) 198 .getNormReference(); 199 } 200 } 201 catch ( LdapException ne ) 202 { 203 String message = I18n.err( I18n.ERR_04447_CANNOT_NORMALIZE_VALUE, ne.getLocalizedMessage() ); 204 LOG.info( message ); 205 } 206 } 207 } 208 else 209 { 210 String message = "The '" + attributeType.getName() + "' AttributeType does not have" + " a normalizer"; 211 LOG.error( message ); 212 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message ); 213 } 214 } 215 else 216 { 217 // No MatchingRule, there is nothing we can do but make the normalized value 218 // to be a reference on the user provided value 219 normalizedValue = upValue; 220 } 221 222 // and checks that the value syntax is valid 223 if ( !attributeType.isRelaxed() ) 224 { 225 try 226 { 227 LdapSyntax syntax = attributeType.getSyntax(); 228 229 // Check the syntax if not in relaxed mode 230 if ( ( syntax != null ) && ( !isValid( syntax.getSyntaxChecker() ) ) ) 231 { 232 String message = I18n.err( I18n.ERR_04473_NOT_VALID_VALUE, upValue, attributeType ); 233 LOG.info( message ); 234 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message ); 235 } 236 } 237 catch ( LdapException le ) 238 { 239 String message = I18n.err( I18n.ERR_04447_CANNOT_NORMALIZE_VALUE, le.getLocalizedMessage() ); 240 LOG.info( message ); 241 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message, le ); 242 } 243 } 244 245 // Rehash the Value now 246 h = 0; 247 hashCode(); 248 } 249 250 251 /** 252 * Gets a comparator using getMatchingRule() to resolve the matching 253 * that the comparator is extracted from. 254 * 255 * @return a comparator associated with the attributeType or null if one cannot be found 256 * @throws LdapException if resolution of schema entities fail 257 */ 258 @SuppressWarnings("unchecked") 259 protected final LdapComparator<T> getLdapComparator() throws LdapException 260 { 261 if ( attributeType != null ) 262 { 263 MatchingRule mr = attributeType.getEquality(); 264 265 if ( mr != null ) 266 { 267 return ( LdapComparator<T> ) mr.getLdapComparator(); 268 } 269 } 270 271 return null; 272 } 273 274 275 /** 276 * {@inheritDoc} 277 */ 278 @Override 279 public boolean isInstanceOf( AttributeType attributeType ) 280 { 281 return ( attributeType != null ) 282 && ( this.attributeType.equals( attributeType ) || this.attributeType.isDescendantOf( attributeType ) ); 283 } 284 285 286 /** 287 * {@inheritDoc} 288 */ 289 @Override 290 public T getNormReference() 291 { 292 if ( isNull() ) 293 { 294 return null; 295 } 296 297 if ( normalizedValue == null ) 298 { 299 return upValue; 300 } 301 302 return normalizedValue; 303 } 304 305 306 /** 307 * {@inheritDoc} 308 */ 309 @Override 310 public final boolean isNull() 311 { 312 return upValue == null; 313 } 314 315 316 /** 317 * {@inheritDoc} 318 */ 319 @Override 320 public final boolean isValid( SyntaxChecker syntaxChecker ) throws LdapInvalidAttributeValueException 321 { 322 if ( syntaxChecker == null ) 323 { 324 String message = I18n.err( I18n.ERR_04139, toString() ); 325 LOG.error( message ); 326 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message ); 327 } 328 329 if ( ( attributeType != null ) && attributeType.isRelaxed() ) 330 { 331 return true; 332 } 333 else 334 { 335 return syntaxChecker.isValidSyntax( normalizedValue ); 336 } 337 } 338 339 340 /** 341 * {@inheritDoc} 342 */ 343 @Override 344 public final boolean isSchemaAware() 345 { 346 return attributeType != null; 347 } 348}