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 */
020 package org.apache.directory.shared.ldap.name;
021
022
023 import javax.naming.InvalidNameException;
024
025 import org.apache.directory.shared.ldap.entry.client.ClientBinaryValue;
026 import org.apache.directory.shared.ldap.entry.client.ClientStringValue;
027 import org.apache.directory.shared.ldap.util.DNUtils;
028 import org.apache.directory.shared.ldap.util.Position;
029 import org.apache.directory.shared.ldap.util.StringTools;
030
031
032 /**
033 * This class parse the name-component part or the following BNF grammar (as of
034 * RFC2253, par. 3, and RFC1779, fig. 1) : <br> - <name-component> ::=
035 * <attributeType> <spaces> '=' <spaces>
036 * <attributeValue> <attributeTypeAndValues> <br> -
037 * <attributeTypeAndValues> ::= <spaces> '+' <spaces>
038 * <attributeType> <spaces> '=' <spaces>
039 * <attributeValue> <attributeTypeAndValues> | e <br> -
040 * <attributeType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9]
041 * <digits> <oids> | [0-9] <digits> <oids> <br> -
042 * <keychars> ::= [a-zA-Z] <keychars> | [0-9] <keychars> | '-'
043 * <keychars> | e <br> - <oidPrefix> ::= 'OID.' | 'oid.' | e <br> -
044 * <oids> ::= '.' [0-9] <digits> <oids> | e <br> -
045 * <attributeValue> ::= <pairs-or-strings> | '#' <hexstring>
046 * |'"' <quotechar-or-pairs> '"' <br> - <pairs-or-strings> ::= '\'
047 * <pairchar> <pairs-or-strings> | <stringchar>
048 * <pairs-or-strings> | e <br> - <quotechar-or-pairs> ::=
049 * <quotechar> <quotechar-or-pairs> | '\' <pairchar>
050 * <quotechar-or-pairs> | e <br> - <pairchar> ::= ',' | '=' | '+' |
051 * '<' | '>' | '#' | ';' | '\' | '"' | [0-9a-fA-F] [0-9a-fA-F] <br> -
052 * <hexstring> ::= [0-9a-fA-F] [0-9a-fA-F] <hexpairs> <br> -
053 * <hexpairs> ::= [0-9a-fA-F] [0-9a-fA-F] <hexpairs> | e <br> -
054 * <digits> ::= [0-9] <digits> | e <br> - <stringchar> ::=
055 * [0x00-0xFF] - [,=+<>#;\"\n\r] <br> - <quotechar> ::= [0x00-0xFF] -
056 * [\"] <br> - <separator> ::= ',' | ';' <br> - <spaces> ::= ' '
057 * <spaces> | e <br>
058 * <br>
059 * A RDN is a part of a DN. It can be composed of many types, as in the RDN
060 * following RDN :<br>
061 * ou=value + cn=other value<br>
062 * <br>
063 * In this case, we have to store an 'ou' and a 'cn' in the RDN.<br>
064 * <br>
065 * The types are case insensitive. <br>
066 * Spaces before and after types and values are not stored.<br>
067 * Spaces before and after '+' are not stored.<br>
068 * <br>
069 * Thus, we can consider that the following RDNs are equals :<br>
070 * <br>
071 * 'ou=test 1'<br> ' ou=test 1'<br>
072 * 'ou =test 1'<br>
073 * 'ou= test 1'<br>
074 * 'ou=test 1 '<br> ' ou = test 1 '<br>
075 * <br>
076 * So are the following :<br>
077 * <br>
078 * 'ou=test 1+cn=test 2'<br>
079 * 'ou = test 1 + cn = test 2'<br> ' ou =test 1+ cn =test 2 ' <br>
080 * 'cn = test 2 +ou = test 1'<br>
081 * <br>
082 * but the following are not equal :<br>
083 * 'ou=test 1' <br>
084 * 'ou=test 1'<br>
085 * because we have more than one spaces inside the value.<br>
086 * <br>
087 *
088 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
089 * @version $Rev: 798550 $, $Date: 2009-07-28 16:54:01 +0200 (Mar, 28 jul 2009) $
090 */
091 public class RdnParser
092 {
093 /**
094 * Parse this rule : <br>
095 * <p>
096 * <oidValue> ::= [0-9] <digits> <oids>
097 * </p>
098 *
099 * @param bytes The bytes array to parse
100 * @param pos The current position in the byte buffer
101 * @return The new position in the vyte array, or PARSING_ERROR if the rule
102 * does not apply to the byte array
103 */
104 private static String parseOidValue( byte[] bytes, Position pos )
105 {
106 pos.start += pos.length;
107 pos.end = pos.start;
108
109 // <attributType> ::= [0-9] <digits> <oids>
110 if ( !StringTools.isDigit( bytes, pos.start ) )
111 {
112 // Nope... An error
113 return null;
114 }
115 else
116 {
117 // Let's process an oid
118 pos.end++;
119
120 while ( StringTools.isDigit( bytes, pos.end ) )
121 {
122 pos.end++;
123 }
124
125 // <oids> ::= '.' [0-9] <digits> <oids> | e
126 if ( !StringTools.isCharASCII( bytes, pos.end, '.' ) )
127 {
128 return null;
129 }
130 else
131 {
132 do
133 {
134 pos.end++;
135
136 if ( !StringTools.isDigit( bytes, pos.end ) )
137 {
138 return null;
139 }
140 else
141 {
142 pos.end++;
143
144 while ( StringTools.isDigit( bytes, pos.end ) )
145 {
146 pos.end++;
147 }
148 }
149
150 }
151 while ( StringTools.isCharASCII( bytes, pos.end, '.' ) );
152
153 return StringTools.utf8ToString( bytes, pos.start - pos.length, pos.end - pos.start + pos.length );
154 }
155 }
156 }
157
158
159 /**
160 * Validate this rule : <br>
161 * <p>
162 * <oidValue> ::= [0-9] <digits> <oids>
163 * </p>
164 *
165 * @param bytes The byte array to parse
166 * @param pos The current position in the byte buffer
167 * @return <code>true</code> if this is a valid OID
168 */
169 private static boolean isValidOidValue( byte[] bytes, Position pos )
170 {
171 pos.start += pos.length;
172 pos.end = pos.start;
173
174 // <attributType> ::= [0-9] <digits> <oids>
175 if ( !StringTools.isDigit( bytes, pos.start ) )
176 {
177 // Nope... An error
178 return false;
179 }
180 else
181 {
182 // Let's process an oid
183 pos.end++;
184
185 while ( StringTools.isDigit( bytes, pos.end ) )
186 {
187 pos.end++;
188 }
189
190 // <oids> ::= '.' [0-9] <digits> <oids> | e
191 if ( !StringTools.isCharASCII( bytes, pos.end, '.' ) )
192 {
193 return false;
194 }
195 else
196 {
197 do
198 {
199 pos.end++;
200
201 if ( !StringTools.isDigit( bytes, pos.end ) )
202 {
203 return false;
204 }
205 else
206 {
207 pos.end++;
208
209 while ( StringTools.isDigit( bytes, pos.end ) )
210 {
211 pos.end++;
212 }
213 }
214
215 }
216 while ( StringTools.isCharASCII( bytes, pos.end, '.' ) );
217
218 return true;
219 }
220 }
221 }
222
223
224 /**
225 * Parse this rule : <br>
226 * <p>
227 * <oidPrefix> ::= 'OID.' | 'oid.' | e
228 * </p>
229 *
230 * @param bytes The buffer to parse
231 * @param pos The current position in the byte array
232 * @return The new position in the byte array, or PARSING_ERROR if the rule
233 * does not apply to the byte array
234 */
235 private static int parseOidPrefix( byte[] bytes, Position pos )
236 {
237 if ( StringTools.isICharASCII( bytes, pos.start, 'O' ) &&
238 StringTools.isICharASCII( bytes, pos.start + 1, 'I' ) &&
239 StringTools.isICharASCII( bytes, pos.start + 2, 'D' ) &&
240 StringTools.isICharASCII( bytes, pos.start + 3, '.' ) )
241 {
242 pos.end += DNUtils.OID_LOWER.length();
243 return DNUtils.PARSING_OK;
244 }
245 else
246 {
247 return DNUtils.PARSING_ERROR;
248 }
249 }
250
251
252 /**
253 * Parse this rule : <br>
254 * <p>
255 * <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix>
256 * [0-9] <digits> <oids> | [0-9] <digits> <oids>
257 * </p>
258 * The string *MUST* be an ASCII string, not an unicode string.
259 *
260 * @param bytes The byte array to parse
261 * @param pos The current position in the byte array
262 * @return The new position in the byte array, or PARSING_ERROR if the rule
263 * does not apply to the byte array
264 */
265 private static String parseAttributeType( byte[] bytes, Position pos )
266 {
267 // <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] <digits>
268 // <oids> | [0-9] <digits> <oids>
269 if ( StringTools.isAlphaASCII( bytes, pos.start ) )
270 {
271 // <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9]
272 // <digits> <oids>
273
274 // We have got an Alpha char, it may be the begining of an OID ?
275 if ( parseOidPrefix( bytes, pos ) != DNUtils.PARSING_ERROR )
276 {
277 pos.length = 4;
278
279 return parseOidValue( bytes, pos );
280 }
281 else
282 {
283 // It's not an oid, it's a String (ASCII)
284 // <attributType> ::= [a-zA-Z] <keychars>
285 // <keychars> ::= [a-zA-Z] <keychar> | [0-9] <keychar> | '-'
286 // <keychar> | e
287 pos.end++;
288
289 while ( StringTools.isAlphaDigitMinus( bytes, pos.end ) )
290 {
291 pos.end++;
292 }
293
294 return StringTools.utf8ToString( bytes, pos.start, pos.end - pos.start );
295 }
296 }
297 else
298 {
299 // An oid
300 // <attributType> ::= [0-9] <digits> <oids>
301 return parseOidValue( bytes, pos );
302 }
303 }
304
305
306 /**
307 * Parse this rule : <br>
308 * <p>
309 * <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix>
310 * [0-9] <digits> <oids> | [0-9] <digits> <oids>
311 * </p>
312 * The string *MUST* be an ASCII string, not an unicode string.
313 *
314 * @param bytes The byte array to parse
315 * @param pos The current position in the byte array
316 * @return The new position in the byte array, or PARSING_ERROR if the rule
317 * does not apply to the byte array
318 */
319 private static boolean isValidAttributeType( byte[] bytes, Position pos )
320 {
321 // <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9] <digits>
322 // <oids> | [0-9] <digits> <oids>
323 if ( StringTools.isAlphaASCII( bytes, pos.start ) )
324 {
325 // <attributType> ::= [a-zA-Z] <keychars> | <oidPrefix> [0-9]
326 // <digits> <oids>
327
328 // We have got an Alpha char, it may be the begining of an OID ?
329 if ( parseOidPrefix( bytes, pos ) != DNUtils.PARSING_ERROR )
330 {
331 pos.length = 4;
332
333 return isValidOidValue( bytes, pos );
334 }
335 else
336 {
337 // It's not an oid, it's a String (ASCII)
338 // <attributType> ::= [a-zA-Z] <keychars>
339 // <keychars> ::= [a-zA-Z] <keychar> | [0-9] <keychar> | '-'
340 // <keychar> | e
341 pos.end++;
342
343 while ( StringTools.isAlphaDigitMinus( bytes, pos.end ) )
344 {
345 pos.end++;
346 }
347
348 return true;
349 }
350 }
351 else
352 {
353 // An oid
354 // <attributType> ::= [0-9] <digits> <oids>
355 return isValidOidValue( bytes, pos );
356 }
357 }
358
359
360 /**
361 * Parse this rule : <br>
362 * <p>
363 * <attributeValue> ::= <pairs-or-strings> | '#'
364 * <hexstring> |'"' <quotechar-or-pairs> '"' <br>
365 * <pairs-or-strings> ::= '\' <pairchar>
366 * <pairs-or-strings> | <stringchar> <pairs-or-strings> | |
367 * e <br>
368 * <quotechar-or-pairs> ::= <quotechar>
369 * <quotechar-or-pairs> | '\' <pairchar>
370 * <quotechar-or-pairs> | e <br>
371 * </p>
372 *
373 * @param bytes The byte array to parse
374 * @param pos The current position in the byte array
375 * @return The new position in the byte array, or PARSING_ERROR if the rule
376 * does not apply to the byte array
377 */
378 private static Object parseAttributeValue( byte[] bytes, Position pos )
379 {
380 if ( pos.start == bytes.length )
381 {
382 // This is an empty value
383 return "";
384 }
385
386 //StringBuffer sb = new StringBuffer();
387 byte c = bytes[pos.start];
388 byte[] buffer = new byte[bytes.length];
389 int currPos = 0;
390
391 if ( c == '#' )
392 {
393 pos.start++;
394 int nbHex = 0;
395 int currentPos = pos.start;
396
397 // First, we will count the number of hexPairs
398 while ( DNUtils.parseHexPair( bytes, currentPos ) >= 0 )
399 {
400 nbHex++;
401 currentPos += DNUtils.TWO_CHARS;
402 }
403
404 byte[] hexValue = new byte[nbHex];
405
406 // Now, convert the value
407 // <attributeValue> ::= '#' <hexstring>
408 if ( DNUtils.parseHexString( bytes, hexValue, pos ) == DNUtils.PARSING_ERROR )
409 {
410 return null;
411 }
412
413 pos.start--;
414 StringTools.trimRight( bytes, pos );
415 pos.length = pos.end - pos.start;
416
417 return hexValue;
418 }
419 else if ( c == '"' )
420 {
421 pos.start++;
422 pos.length = 0;
423 pos.end = pos.start;
424 int nbBytes = 0;
425 int length = 0;
426
427 // <attributeValue> ::= '"' <quotechar-or-pair> '"'
428 // <quotechar-or-pairs> ::= <quotechar> <quotechar-or-pairs> | '\'
429 // <pairchar> <quotechar-or-pairs> | e
430 while ( true )
431 {
432 if ( StringTools.isCharASCII( bytes, pos.end, '\\' ) )
433 {
434 pos.end++;
435 int nbChars = 0;
436
437 if ( ( nbChars = DNUtils.countPairChar( bytes, pos.end ) ) == DNUtils.PARSING_ERROR )
438 {
439 return null;
440 }
441 else
442 {
443 if ( nbChars == 1 )
444 {
445 buffer[currPos++] = bytes[pos.end];
446 }
447 else
448 {
449 byte b = StringTools.getHexValue( bytes[pos.end], bytes[pos.end + 1] );
450
451 buffer[currPos++] = b;
452 }
453
454 pos.end += nbChars;
455 length += nbChars;
456 }
457 }
458 else if ( ( nbBytes = DNUtils.isQuoteChar( bytes, pos.end ) ) != DNUtils.PARSING_ERROR )
459 {
460 for ( int i = 0; i < nbBytes; i++ )
461 {
462 buffer[currPos++] = bytes[pos.end + i];
463 }
464
465 pos.end += nbBytes;
466 length += nbBytes;
467 }
468 else
469 {
470 //pos.length = pos.end - pos.start;
471 break;
472 }
473 }
474
475 if ( StringTools.isCharASCII( bytes, pos.end, '"' ) )
476 {
477 pos.end++;
478 pos.length = length + 2;
479 return StringTools.utf8ToString( buffer, length );
480 }
481 else
482 {
483 return null;
484 }
485 }
486 else
487 {
488 int escapedSpace = -1;
489
490 while ( true )
491 {
492 if ( StringTools.isCharASCII( bytes, pos.end, '\\' ) )
493 {
494 // '\' <pairchar> <pairs-or-strings>
495 pos.end++;
496
497 int nbChars = 0;
498
499 if ( ( nbChars = DNUtils.countPairChar( bytes, pos.end ) ) == DNUtils.PARSING_ERROR )
500 {
501 return null;
502 }
503 else
504 {
505 if ( nbChars == 1 )
506 {
507 buffer[currPos++] = bytes[pos.end];
508 }
509 else
510 {
511 byte b = StringTools.getHexValue( bytes[pos.end], bytes[pos.end + 1] );
512
513 buffer[currPos++] = b;
514 }
515
516 if ( bytes[pos.end] == ' ' )
517 {
518 escapedSpace = currPos;
519 }
520 else
521 {
522 escapedSpace = -1;
523 }
524
525 pos.end += nbChars;
526 }
527 }
528 else
529 {
530 int nbChars = 0;
531
532 // <stringchar> <pairs-or-strings>
533 if ( ( nbChars = DNUtils.isStringChar( bytes, pos.end ) ) != DNUtils.PARSING_ERROR )
534 {
535 // If the char is not a space, we have to switch off the escapedSpace flag
536 if ( ( escapedSpace != -1 ) && ! StringTools.isCharASCII( bytes, pos.end, ' ' ) )
537 {
538 escapedSpace = -1;
539 }
540
541 // A special case : if we have some spaces before the
542 // '+' character,
543 // we MUST skip them.
544 if ( StringTools.isCharASCII( bytes, pos.end, '+' ) )
545 {
546 //StringTools.trimLeft( string, pos );
547
548 if ( ( DNUtils.isStringChar( bytes, pos.end ) == DNUtils.PARSING_ERROR )
549 && ( !StringTools.isCharASCII( bytes, pos.end, '\\' ) ) )
550 {
551 // Ok, we are done with the stringchar.
552 String result = StringTools.trimRight(
553 StringTools.utf8ToString( bytes, pos.start, pos.start + pos.length ) );
554
555 return result;
556 }
557 else
558 {
559 for ( int i = 0; i < nbChars; i++ )
560 {
561 buffer[currPos++] = bytes[pos.end];
562 pos.end ++;
563 }
564 }
565 }
566 else
567 {
568 for ( int i = 0; i < nbChars; i++ )
569 {
570 buffer[currPos++] = bytes[pos.end];
571 pos.end ++;
572 }
573 }
574 }
575 else
576 {
577 pos.length = pos.end - pos.start;
578
579 if ( escapedSpace == -1 )
580 {
581 String result = StringTools.trimRight( StringTools.utf8ToString( buffer, currPos ) );
582 return result;
583 }
584
585 String result = StringTools.utf8ToString( buffer, escapedSpace );
586
587 return result;
588 }
589 }
590 }
591 }
592 }
593
594
595 /**
596 * Validate this rule : <br>
597 * <p>
598 * <attributeValue> ::= <pairs-or-strings> | '#'
599 * <hexstring> |'"' <quotechar-or-pairs> '"' <br>
600 * <pairs-or-strings> ::= '\' <pairchar>
601 * <pairs-or-strings> | <stringchar> <pairs-or-strings> | |
602 * e <br>
603 * <quotechar-or-pairs> ::= <quotechar>
604 * <quotechar-or-pairs> | '\' <pairchar>
605 * <quotechar-or-pairs> | e <br>
606 * </p>
607 *
608 * @param bytes The byte array to parse
609 * @param pos The current position in the byte array
610 * @return <code>true</code> if the rule is valid
611 */
612 private static boolean isValidAttributeValue( byte[] bytes, Position pos )
613 {
614 if( bytes.length <= pos.start )
615 {
616 // no attribute value
617 return true;
618 }
619
620 byte c = bytes[pos.start];
621
622 if ( c == '#' )
623 {
624 pos.start++;
625 int nbHex = 0;
626 int currentPos = pos.start;
627
628 // First, we will count the number of hexPairs
629 while ( DNUtils.parseHexPair( bytes, currentPos ) >= 0 )
630 {
631 nbHex++;
632 currentPos += DNUtils.TWO_CHARS;
633 }
634
635 byte[] hexValue = new byte[nbHex];
636
637 // Now, convert the value
638 // <attributeValue> ::= '#' <hexstring>
639 if ( DNUtils.parseHexString( bytes, hexValue, pos ) == DNUtils.PARSING_ERROR )
640 {
641 return false;
642 }
643
644 pos.start--;
645 StringTools.trimRight( bytes, pos );
646 pos.length = pos.end - pos.start;
647
648 return true;
649 }
650 else if ( c == '"' )
651 {
652 pos.start++;
653 pos.length = 0;
654 pos.end = pos.start;
655 int nbBytes = 0;
656
657 // <attributeValue> ::= '"' <quotechar-or-pair> '"'
658 // <quotechar-or-pairs> ::= <quotechar> <quotechar-or-pairs> | '\'
659 // <pairchar> <quotechar-or-pairs> | e
660 while ( true )
661 {
662 if ( StringTools.isCharASCII( bytes, pos.end, '\\' ) )
663 {
664 pos.end++;
665 int nbChars = 0;
666
667 if ( ( nbChars = DNUtils.countPairChar( bytes, pos.end ) ) != DNUtils.PARSING_ERROR )
668 {
669 pos.end += nbChars;
670 }
671 else
672 {
673 return false;
674 }
675 }
676 else if ( ( nbBytes = DNUtils.isQuoteChar( bytes, pos.end ) ) != DNUtils.PARSING_ERROR )
677 {
678 pos.end += nbBytes;
679 }
680 else
681 {
682 pos.length = pos.end - pos.start;
683 break;
684 }
685 }
686
687 if ( StringTools.isCharASCII( bytes, pos.end, '"' ) )
688 {
689 pos.end++;
690 return true;
691 }
692 else
693 {
694 return false;
695 }
696 }
697 else
698 {
699 while ( true )
700 {
701 if ( StringTools.isCharASCII( bytes, pos.end, '\\' ) )
702 {
703 // '\' <pairchar> <pairs-or-strings>
704 pos.end++;
705
706 int nbChars = 0;
707
708 if ( ( nbChars = DNUtils.countPairChar( bytes, pos.end ) ) == DNUtils.PARSING_ERROR )
709 {
710 return false;
711 }
712 else
713 {
714 pos.end += nbChars;
715 }
716 }
717 else
718 {
719 int nbChars = 0;
720
721 // <stringchar> <pairs-or-strings>
722 if ( ( nbChars = DNUtils.isStringChar( bytes, pos.end ) ) != DNUtils.PARSING_ERROR )
723 {
724 // A special case : if we have some spaces before the
725 // '+' character,
726 // we MUST skip them.
727 if ( StringTools.isCharASCII( bytes, pos.end, '+' ) )
728 {
729 //StringTools.trimLeft( string, pos );
730
731 if ( ( DNUtils.isStringChar( bytes, pos.end ) == DNUtils.PARSING_ERROR )
732 && ( !StringTools.isCharASCII( bytes, pos.end, '\\' ) ) )
733 {
734 // Ok, we are done with the stringchar.
735 return true;
736 }
737 else
738 {
739 pos.end++;
740 }
741 }
742 else
743 {
744 pos.end += nbChars;
745 }
746 }
747 else
748 {
749 return true;
750 }
751 }
752 }
753 }
754 }
755
756
757 /**
758 * Parse this rule : <br>
759 * <p>
760 * <nameComponents> ::= <spaces> '+' <spaces>
761 * <attributeType> <spaces> '=' <spaces>
762 * <attributeValue> <nameComponents> | e
763 * </p>
764 *
765 * @param bytes The byte buffer to parse
766 * @param pos The current position in the byte buffer
767 * @param rdn The rdn to generate
768 * @return The new position in the byte buffer, or PARSING_ERROR if the rule
769 * does not apply to the byte buffer
770 * @throws InvalidNameException If the NameComponent is invalid
771 */
772 private static int parseNameComponents( byte[] bytes, Position pos, Rdn rdn ) throws InvalidNameException
773 {
774 if ( rdn == null )
775 {
776 throw new InvalidNameException( "The RDN should not be null" );
777 }
778
779 int newStart = 0;
780 String type = null;
781 Object value = null;
782
783 while ( true )
784 {
785 StringTools.trimLeft( bytes, pos );
786
787 if ( StringTools.isCharASCII( bytes, pos.end, '+' ) )
788 {
789 pos.start++;
790 pos.length = 0;
791 }
792 else
793 {
794 // <attributeTypeAndValues> ::= e
795 rdn.normalize();
796 return DNUtils.PARSING_OK;
797 }
798
799 StringTools.trimLeft( bytes, pos );
800
801 if ( ( type = parseAttributeType( bytes, pos ) ) == null )
802 {
803 return DNUtils.PARSING_ERROR;
804 }
805
806 pos.start = pos.end;
807
808 StringTools.trimLeft( bytes, pos );
809
810 if ( StringTools.isCharASCII( bytes, pos.end, '=' ) )
811 {
812 pos.start++;
813 }
814 else
815 {
816 return DNUtils.PARSING_ERROR;
817 }
818
819 StringTools.trimLeft( bytes, pos );
820
821 value = parseAttributeValue( bytes, pos );
822
823 newStart = pos.end;
824
825 if ( value != null )
826 {
827 if ( rdn != null )
828 {
829 if ( value instanceof String )
830 {
831 rdn.addAttributeTypeAndValue( type, type,
832 new ClientStringValue( (String)value ),
833 new ClientStringValue( (String)value ) );
834 }
835 else
836 {
837 rdn.addAttributeTypeAndValue( type, type,
838 new ClientBinaryValue( (byte[])value ),
839 new ClientBinaryValue( (byte[])value ) );
840 }
841 }
842 }
843
844 pos.start = newStart;
845 pos.end = newStart;
846 }
847 }
848
849
850 /**
851 * Validate this rule : <br>
852 * <p>
853 * <nameComponents> ::= <spaces> '+' <spaces>
854 * <attributeType> <spaces> '=' <spaces>
855 * <attributeValue> <nameComponents> | e
856 * </p>
857 *
858 * @param bytes The byte buffer to parse
859 * @param pos The current position in the byte buffer
860 * @param isFirstRdn A flag set if the RDN is the first one
861 * @return <code>true</code> if the rule is valid
862 */
863 private static boolean isValidNameComponents( byte[] bytes, Position pos, boolean isFirstRdn )
864 {
865 int newStart = 0;
866
867 while ( true )
868 {
869 StringTools.trimLeft( bytes, pos );
870
871 if ( StringTools.isCharASCII( bytes, pos.end, '+' ) )
872 {
873 pos.start++;
874 }
875 else
876 {
877 // <attributeTypeAndValues> ::= e
878 return true;
879 }
880
881 StringTools.trimLeft( bytes, pos );
882
883 if ( !isValidAttributeType( bytes, pos ) )
884 {
885 return false;
886 }
887
888 pos.start = pos.end;
889
890 StringTools.trimLeft( bytes, pos );
891
892 if ( StringTools.isCharASCII( bytes, pos.end, '=' ) )
893 {
894 pos.start++;
895 }
896 else
897 {
898 return false;
899 }
900
901 StringTools.trimLeft( bytes, pos );
902
903 if ( !isValidAttributeValue( bytes, pos ) )
904 {
905 return false;
906 }
907
908 newStart = pos.end;
909 pos.start = newStart;
910 pos.end = newStart;
911 }
912 }
913
914
915 /**
916 * Parse a NameComponent : <br>
917 * <p>
918 * <name-component> ::= <attributeType> <spaces> '='
919 * <spaces> <attributeValue> <nameComponents>
920 * </p>
921 *
922 * @param dn The String to parse
923 * @param pos The current position in the buffer
924 * @param rdn The constructed RDN
925 * @return The new position in the char array, or PARSING_ERROR if the rule
926 * does not apply to the char array
927 * @throws InvalidNameException If the NameComponent is invalid
928 */
929 public static int parse( String dn, Position pos, Rdn rdn ) throws InvalidNameException
930 {
931 return parse( StringTools.getBytesUtf8( dn ), pos, rdn );
932 }
933
934
935 /**
936 * Parse a NameComponent : <br>
937 * <p>
938 * <name-component> ::= <attributeType> <spaces> '='
939 * <spaces> <attributeValue> <nameComponents>
940 * </p>
941 *
942 * @param dn The byte array to parse
943 * @param pos The current position in the buffer
944 * @param rdn The constructed RDN
945 * @return The new position in the byte array, or PARSING_ERROR if the rule
946 * does not apply to the byte array
947 * @throws InvalidNameException If the NameComponent is invalid
948 */
949 public static int parse( byte[] dn, Position pos, Rdn rdn ) throws InvalidNameException
950 {
951 if ( rdn == null )
952 {
953 throw new InvalidNameException( "Cannot feed a null RDN structure" );
954 }
955
956 String type = null;
957 Object value = null;
958 int start = pos.start;
959
960 StringTools.trimLeft( dn, pos );
961
962 pos.end = pos.start;
963 pos.length = 0;
964
965 if ( ( type = parseAttributeType( dn, pos ) ) == null )
966 {
967 return DNUtils.PARSING_ERROR;
968 }
969
970 pos.start = pos.end;
971
972 StringTools.trimLeft( dn, pos );
973
974 if ( !StringTools.isCharASCII( dn, pos.start, '=' ) )
975 {
976 return DNUtils.PARSING_ERROR;
977 }
978 else
979 {
980 pos.start++;
981 }
982
983 StringTools.trimLeft( dn, pos );
984
985 pos.end = pos.start;
986
987 int start2 = pos.start;
988
989 if ( ( value = parseAttributeValue( dn, pos ) ) == null )
990 {
991 return DNUtils.PARSING_ERROR;
992 }
993
994 String upValue = StringTools.utf8ToString( dn, start2, pos.length );
995
996 if ( value instanceof String )
997 {
998 rdn.addAttributeTypeAndValue( type, type,
999 new ClientStringValue( upValue ),
1000 new ClientStringValue( (String)value ) );
1001 }
1002 else
1003 {
1004 rdn.addAttributeTypeAndValue( type, type,
1005 new ClientStringValue( upValue ),
1006 new ClientBinaryValue( (byte[])value ) );
1007 }
1008
1009 rdn.normalize();
1010
1011 pos.start = pos.end;
1012 pos.length = 0;
1013
1014 if ( parseNameComponents( dn, pos, rdn ) == DNUtils.PARSING_ERROR )
1015 {
1016 return DNUtils.PARSING_ERROR;
1017 }
1018
1019 rdn.setUpName( StringTools.utf8ToString( dn, start, pos.end - start ) );
1020 pos.start = pos.end;
1021 return DNUtils.PARSING_OK;
1022 }
1023
1024
1025 /**
1026 * Validate a NameComponent : <br>
1027 * <p>
1028 * <name-component> ::= <attributeType> <spaces> '='
1029 * <spaces> <attributeValue> <nameComponents>
1030 * </p>
1031 *
1032 * @param dn The byte array to parse
1033 * @param pos The current position in the buffer
1034 * @param isFirstRdn a flag set if the RDN is the first for the current DN
1035 * @return <code>true</code> if the RDN is valid
1036 */
1037 public static boolean isValid( byte[] dn, Position pos, boolean isFirstRdn )
1038 {
1039 StringTools.trimLeft( dn, pos );
1040
1041 pos.end = pos.start;
1042 pos.length = 0;
1043
1044 if ( !isValidAttributeType( dn, pos ) )
1045 {
1046 return false;
1047 }
1048
1049 pos.start = pos.end;
1050
1051 StringTools.trimLeft( dn, pos );
1052
1053 if ( !StringTools.isCharASCII( dn, pos.start, '=' ) )
1054 {
1055 return false;
1056 }
1057 else
1058 {
1059 pos.start++;
1060 }
1061
1062 StringTools.trimLeft( dn, pos );
1063
1064 pos.end = pos.start;
1065
1066 if ( !isValidAttributeValue( dn, pos ) )
1067 {
1068 return false;
1069 }
1070
1071 pos.start = pos.end;
1072 pos.length = 0;
1073
1074 if ( !isValidNameComponents( dn, pos, isFirstRdn ) )
1075 {
1076 return false;
1077 }
1078
1079 pos.start = pos.end;
1080 return true;
1081 }
1082
1083
1084 /**
1085 * Parse a NameComponent : <br>
1086 * <p>
1087 * <name-component> ::= <attributeType> <spaces> '='
1088 * <spaces> <attributeValue> <nameComponents>
1089 * </p>
1090 *
1091 * @param dn The String to parse
1092 * @param rdn The RDN to fill. Beware that if the RDN is not empty, the new
1093 * AttributeTypeAndValue will be added.
1094 * @throws InvalidNameException If the NameComponent is invalid
1095 */
1096 public static void parse( String dn, Rdn rdn ) throws InvalidNameException
1097 {
1098 if ( parse( StringTools.getBytesUtf8( dn ), new Position(), rdn ) == DNUtils.PARSING_ERROR )
1099 {
1100 throw new InvalidNameException( "The RDN can't be parsed." );
1101 }
1102 }
1103
1104 /**
1105 * Validate a NameComponent : <br>
1106 * <p>
1107 * <name-component> ::= <attributeType> <spaces> '='
1108 * <spaces> <attributeValue> <nameComponents>
1109 * </p>
1110 *
1111 * @param dn The string to parse
1112 * @return <code>true</code> if the RDN is valid
1113 */
1114 public static boolean isValid( String dn )
1115 {
1116 return isValid( StringTools.getBytesUtf8( dn ), new Position(), false );
1117 }
1118 }