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 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 041 */ 042public abstract class AbstractValue<T> implements Value<T> 043{ 044 /** logger for reporting errors that might not be handled properly upstream */ 045 protected static final Logger LOG = LoggerFactory.getLogger( AbstractValue.class ); 046 047 /** reference to the attributeType zssociated with the value */ 048 protected transient AttributeType attributeType; 049 050 /** the wrapped binary value */ 051 protected T wrappedValue; 052 053 /** the canonical representation of the wrapped value */ 054 protected T normalizedValue; 055 056 /** The computed hashcode. We don't want to compute it each time the hashcode() method is called */ 057 protected volatile int h; 058 059 060 /** 061 * {@inheritDoc} 062 */ 063 @SuppressWarnings("unchecked") 064 public Value<T> clone() 065 { 066 try 067 { 068 return ( Value<T> ) super.clone(); 069 } 070 catch ( CloneNotSupportedException cnse ) 071 { 072 // Do nothing 073 return null; 074 } 075 } 076 077 078 /** 079 * {@inheritDoc} 080 */ 081 public T getReference() 082 { 083 return wrappedValue; 084 } 085 086 087 /** 088 * Get the wrapped value as a String. 089 * 090 * @return the wrapped value as a String 091 */ 092 public String getString() 093 { 094 throw new UnsupportedOperationException( "Cannot call this method on a binary value" ); 095 } 096 097 098 /** 099 * Get the wrapped value as a byte[]. 100 * 101 * @return the wrapped value as a byte[] 102 */ 103 public byte[] getBytes() 104 { 105 throw new UnsupportedOperationException( "Cannot call this method on a String value" ); 106 } 107 108 109 /** 110 * {@inheritDoc} 111 */ 112 public AttributeType getAttributeType() 113 { 114 return attributeType; 115 } 116 117 118 /** 119 * Apply an AttributeType to the current Value, normalizing it. 120 * 121 * @param attributeType The AttributeType to apply 122 * @throws LdapInvalidAttributeValueException If the value is not valid accordingly 123 * to the schema 124 */ 125 @SuppressWarnings("unchecked") 126 protected void apply( AttributeType attributeType ) throws LdapInvalidAttributeValueException 127 { 128 if ( attributeType == null ) 129 { 130 // No attributeType : the normalized value and the user provided value are the same 131 normalizedValue = wrappedValue; 132 return; 133 } 134 135 this.attributeType = attributeType; 136 137 // We first have to normalize the value before we can check its syntax 138 // Get the equality matchingRule, if we have one 139 MatchingRule equality = attributeType.getEquality(); 140 141 if ( equality != null ) 142 { 143 // If we have an Equality MR, we *must* have a normalizer 144 Normalizer normalizer = equality.getNormalizer(); 145 146 if ( normalizer != null ) 147 { 148 if ( wrappedValue != null ) 149 { 150 boolean isHR = attributeType.getSyntax().isHumanReadable(); 151 152 if ( isHR != isHumanReadable() ) 153 { 154 String message = "The '" + attributeType.getName() + "' AttributeType and values must " + 155 "both be String or binary"; 156 LOG.error( message ); 157 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message ); 158 } 159 160 try 161 { 162 if ( isHumanReadable() ) 163 { 164 normalizedValue = ( T ) normalizer.normalize( ( String ) wrappedValue ); 165 } 166 else 167 { 168 normalizedValue = ( T ) normalizer.normalize( new BinaryValue( ( byte[] ) wrappedValue ) ) 169 .getNormReference(); 170 } 171 } 172 catch ( LdapException ne ) 173 { 174 String message = I18n.err( I18n.ERR_04447_CANNOT_NORMALIZE_VALUE, ne.getLocalizedMessage() ); 175 LOG.info( message ); 176 } 177 } 178 } 179 else 180 { 181 String message = "The '" + attributeType.getName() + "' AttributeType does not have" + 182 " a normalizer"; 183 LOG.error( message ); 184 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message ); 185 } 186 } 187 else 188 { 189 // No MatchingRule, there is nothing we can do but make the normalized value 190 // to be a reference on the user provided value 191 normalizedValue = wrappedValue; 192 } 193 194 // and checks that the value syntax is valid 195 try 196 { 197 LdapSyntax syntax = attributeType.getSyntax(); 198 199 // Check the syntax 200 if ( ( syntax != null ) && ( !isValid( syntax.getSyntaxChecker() ) ) ) 201 { 202 String message = I18n.err( I18n.ERR_04473_NOT_VALID_VALUE, wrappedValue, attributeType ); 203 LOG.info( message ); 204 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message ); 205 } 206 } 207 catch ( LdapException le ) 208 { 209 String message = I18n.err( I18n.ERR_04447_CANNOT_NORMALIZE_VALUE, le.getLocalizedMessage() ); 210 LOG.info( message ); 211 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message, le ); 212 } 213 214 // Rehash the Value now 215 h = 0; 216 hashCode(); 217 } 218 219 220 /** 221 * Gets a comparator using getMatchingRule() to resolve the matching 222 * that the comparator is extracted from. 223 * 224 * @return a comparator associated with the attributeType or null if one cannot be found 225 * @throws LdapException if resolution of schema entities fail 226 */ 227 @SuppressWarnings("unchecked") 228 protected final LdapComparator<T> getLdapComparator() throws LdapException 229 { 230 if ( attributeType != null ) 231 { 232 MatchingRule mr = attributeType.getEquality(); 233 234 if ( mr != null ) 235 { 236 return ( LdapComparator<T> ) mr.getLdapComparator(); 237 } 238 } 239 240 return null; 241 } 242 243 244 /** 245 * {@inheritDoc} 246 */ 247 public boolean isInstanceOf( AttributeType attributeType ) 248 { 249 return ( attributeType != null ) && 250 ( this.attributeType.equals( attributeType ) || 251 this.attributeType.isDescendantOf( attributeType ) ); 252 } 253 254 255 /** 256 * {@inheritDoc} 257 */ 258 public T getNormReference() 259 { 260 if ( isNull() ) 261 { 262 return null; 263 } 264 265 if ( normalizedValue == null ) 266 { 267 return wrappedValue; 268 } 269 270 return normalizedValue; 271 } 272 273 274 /** 275 * {@inheritDoc} 276 */ 277 public final boolean isNull() 278 { 279 return wrappedValue == null; 280 } 281 282 283 /** 284 * {@inheritDoc} 285 */ 286 public final boolean isValid( SyntaxChecker syntaxChecker ) throws LdapInvalidAttributeValueException 287 { 288 if ( syntaxChecker == null ) 289 { 290 String message = I18n.err( I18n.ERR_04139, toString() ); 291 LOG.error( message ); 292 throw new LdapInvalidAttributeValueException( ResultCodeEnum.INVALID_ATTRIBUTE_SYNTAX, message ); 293 } 294 295 return syntaxChecker.isValidSyntax( normalizedValue ); 296 } 297 298 299 /** 300 * {@inheritDoc} 301 */ 302 public final boolean isSchemaAware() 303 { 304 return attributeType != null; 305 } 306}