001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.schema;
028 import org.opends.messages.Message;
029
030
031
032 import java.util.ArrayList;
033 import java.util.HashMap;
034 import java.util.LinkedHashMap;
035 import java.util.LinkedList;
036 import java.util.List;
037
038 import org.opends.server.admin.std.server.*;
039 import org.opends.server.admin.server.ConfigurationChangeListener;
040 import org.opends.server.api.ApproximateMatchingRule;
041 import org.opends.server.api.AttributeSyntax;
042 import org.opends.server.api.EqualityMatchingRule;
043 import org.opends.server.api.OrderingMatchingRule;
044 import org.opends.server.api.SubstringMatchingRule;
045 import org.opends.server.config.ConfigException;
046 import org.opends.server.core.DirectoryServer;
047
048 import static org.opends.server.loggers.debug.DebugLogger.*;
049 import org.opends.server.loggers.debug.DebugTracer;
050 import org.opends.server.types.*;
051 import static org.opends.messages.SchemaMessages.*;
052
053 import org.opends.messages.MessageBuilder;
054 import static org.opends.server.schema.SchemaConstants.*;
055 import static org.opends.server.util.ServerConstants.*;
056 import static org.opends.server.util.StaticUtils.*;
057
058
059
060 /**
061 * This class defines the attribute type description syntax, which is used to
062 * hold attribute type definitions in the server schema. The format of this
063 * syntax is defined in RFC 2252.
064 */
065 public class AttributeTypeSyntax
066 extends AttributeSyntax<AttributeTypeDescriptionAttributeSyntaxCfg>
067 implements
068 ConfigurationChangeListener<AttributeTypeDescriptionAttributeSyntaxCfg> {
069
070 /**
071 * The tracer object for the debug logger.
072 */
073 private static final DebugTracer TRACER = getTracer();
074
075
076
077 // The reference to the configuration for this attribute type description
078 // syntax.
079 private AttributeTypeDescriptionAttributeSyntaxCfg currentConfig;
080
081
082
083 // The default equality matching rule for this syntax.
084 private EqualityMatchingRule defaultEqualityMatchingRule;
085
086 // The default ordering matching rule for this syntax.
087 private OrderingMatchingRule defaultOrderingMatchingRule;
088
089 // The default substring matching rule for this syntax.
090 private SubstringMatchingRule defaultSubstringMatchingRule;
091
092 // If true strip the suggested minimum upper bound from the syntax OID.
093 private static boolean stripMinimumUpperBound=false;
094
095
096 /**
097 * Creates a new instance of this syntax. Note that the only thing that
098 * should be done here is to invoke the default constructor for the
099 * superclass. All initialization should be performed in the
100 * <CODE>initializeSyntax</CODE> method.
101 */
102 public AttributeTypeSyntax()
103 {
104 super();
105 }
106
107
108
109 /**
110 * {@inheritDoc}
111 */
112 public void
113 initializeSyntax(AttributeTypeDescriptionAttributeSyntaxCfg configuration)
114 throws ConfigException, InitializationException
115 {
116 defaultEqualityMatchingRule =
117 DirectoryServer.getEqualityMatchingRule(EMR_CASE_IGNORE_OID);
118 if (defaultEqualityMatchingRule == null)
119 {
120 Message message = ERR_ATTR_SYNTAX_UNKNOWN_EQUALITY_MATCHING_RULE.get(
121 EMR_CASE_IGNORE_OID, SYNTAX_ATTRIBUTE_TYPE_NAME);
122 throw new InitializationException(message);
123 }
124
125 defaultOrderingMatchingRule =
126 DirectoryServer.getOrderingMatchingRule(OMR_CASE_IGNORE_OID);
127 if (defaultOrderingMatchingRule == null)
128 {
129 Message message = ERR_ATTR_SYNTAX_UNKNOWN_ORDERING_MATCHING_RULE.get(
130 OMR_CASE_IGNORE_OID, SYNTAX_ATTRIBUTE_TYPE_NAME);
131 throw new InitializationException(message);
132 }
133
134 defaultSubstringMatchingRule =
135 DirectoryServer.getSubstringMatchingRule(SMR_CASE_IGNORE_OID);
136 if (defaultSubstringMatchingRule == null)
137 {
138 Message message = ERR_ATTR_SYNTAX_UNKNOWN_SUBSTRING_MATCHING_RULE.get(
139 SMR_CASE_IGNORE_OID, SYNTAX_ATTRIBUTE_TYPE_NAME);
140 throw new InitializationException(message);
141 }
142
143 // This syntax is one of the Directory Server's core syntaxes and therefore
144 // it may be instantiated at times without a configuration entry. If that
145 // is the case, then we'll exit now before doing anything that could require
146 // access to that entry.
147 if (configuration == null)
148 {
149 return;
150 }
151
152 currentConfig = configuration;
153 currentConfig.addAttributeTypeDescriptionChangeListener(this);
154 stripMinimumUpperBound=configuration.isStripSyntaxMinUpperBound();
155 }
156
157
158
159 /**
160 * {@inheritDoc}
161 */
162 public String getSyntaxName()
163 {
164 return SYNTAX_ATTRIBUTE_TYPE_NAME;
165 }
166
167
168
169 /**
170 * {@inheritDoc}
171 */
172 public String getOID()
173 {
174 return SYNTAX_ATTRIBUTE_TYPE_OID;
175 }
176
177
178
179 /**
180 * {@inheritDoc}
181 */
182 public String getDescription()
183 {
184 return SYNTAX_ATTRIBUTE_TYPE_DESCRIPTION;
185 }
186
187
188
189 /**
190 * {@inheritDoc}
191 */
192 public EqualityMatchingRule getEqualityMatchingRule()
193 {
194 return defaultEqualityMatchingRule;
195 }
196
197
198
199 /**
200 * {@inheritDoc}
201 */
202 public OrderingMatchingRule getOrderingMatchingRule()
203 {
204 return defaultOrderingMatchingRule;
205 }
206
207
208
209 /**
210 * {@inheritDoc}
211 */
212 public SubstringMatchingRule getSubstringMatchingRule()
213 {
214 return defaultSubstringMatchingRule;
215 }
216
217
218
219 /**
220 * {@inheritDoc}
221 */
222 public ApproximateMatchingRule getApproximateMatchingRule()
223 {
224 // There is no approximate matching rule by default.
225 return null;
226 }
227
228
229
230 /**
231 * {@inheritDoc}
232 */
233 public boolean valueIsAcceptable(ByteString value,
234 MessageBuilder invalidReason)
235 {
236 // We'll use the decodeAttributeType method to determine if the value is
237 // acceptable.
238 try
239 {
240 decodeAttributeType(value, DirectoryServer.getSchema(), true);
241 return true;
242 }
243 catch (DirectoryException de)
244 {
245 if (debugEnabled())
246 {
247 TRACER.debugCaught(DebugLogLevel.ERROR, de);
248 }
249
250 invalidReason.append(de.getMessageObject());
251 return false;
252 }
253 }
254
255
256
257 /**
258 * Decodes the contents of the provided ASN.1 octet string as an attribute
259 * type definition according to the rules of this syntax. Note that the
260 * provided octet string value does not need to be normalized (and in fact, it
261 * should not be in order to allow the desired capitalization to be
262 * preserved).
263 *
264 * @param value The ASN.1 octet string containing the value
265 * to decode (it does not need to be
266 * normalized).
267 * @param schema The schema to use to resolve references to
268 * other schema elements.
269 * @param allowUnknownElements Indicates whether to allow values that
270 * reference a superior attribute type which are
271 * not defined in the server schema. This should
272 * only be true when called by
273 * {@code valueIsAcceptable}.
274 *
275 * @return The decoded attribute type definition.
276 *
277 * @throws DirectoryException If the provided value cannot be decoded as an
278 * attribute type definition.
279 */
280 public static AttributeType decodeAttributeType(ByteString value,
281 Schema schema,
282 boolean allowUnknownElements)
283 throws DirectoryException
284 {
285 // Get string representations of the provided value using the provided form
286 // and with all lowercase characters.
287 String valueStr = value.stringValue();
288 String lowerStr = toLowerCase(valueStr);
289
290
291 // We'll do this a character at a time. First, skip over any leading
292 // whitespace.
293 int pos = 0;
294 int length = valueStr.length();
295 while ((pos < length) && (valueStr.charAt(pos) == ' '))
296 {
297 pos++;
298 }
299
300 if (pos >= length)
301 {
302 // This means that the value was empty or contained only whitespace. That
303 // is illegal.
304 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_EMPTY_VALUE.get();
305 throw new DirectoryException(
306 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
307 }
308
309
310 // The next character must be an open parenthesis. If it is not, then that
311 // is an error.
312 char c = valueStr.charAt(pos++);
313 if (c != '(')
314 {
315 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_EXPECTED_OPEN_PARENTHESIS.get(
316 valueStr, (pos-1), String.valueOf(c));
317 throw new DirectoryException(
318 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
319 }
320
321
322 // Skip over any spaces immediately following the opening parenthesis.
323 while ((pos < length) && ((c = valueStr.charAt(pos)) == ' '))
324 {
325 pos++;
326 }
327
328 if (pos >= length)
329 {
330 // This means that the end of the value was reached before we could find
331 // the OID. Ths is illegal.
332 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_TRUNCATED_VALUE.get(valueStr);
333 throw new DirectoryException(
334 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
335 }
336
337
338 // The next set of characters must be the OID. Strictly speaking, this
339 // should only be a numeric OID, but we'll also allow for the
340 // "attrname-oid" case as well. Look at the first character to figure out
341 // which we will be using.
342 int oidStartPos = pos;
343 if (isDigit(c))
344 {
345 // This must be a numeric OID. In that case, we will accept only digits
346 // and periods, but not consecutive periods.
347 boolean lastWasPeriod = false;
348 while ((pos < length) && ((c = valueStr.charAt(pos++)) != ' '))
349 {
350 if (c == '.')
351 {
352 if (lastWasPeriod)
353 {
354 Message message =
355 ERR_ATTR_SYNTAX_ATTRTYPE_DOUBLE_PERIOD_IN_NUMERIC_OID.
356 get(valueStr, (pos-1));
357 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
358 message);
359 }
360 else
361 {
362 lastWasPeriod = true;
363 }
364 }
365 else if (! isDigit(c))
366 {
367 // This must have been an illegal character.
368 Message message =
369 ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR_IN_NUMERIC_OID.
370 get(valueStr, String.valueOf(c), (pos-1));
371 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
372 message);
373 }
374 else
375 {
376 lastWasPeriod = false;
377 }
378 }
379 }
380 else
381 {
382 // This must be a "fake" OID. In this case, we will only accept
383 // alphabetic characters, numeric digits, and the hyphen.
384 while ((pos < length) && ((c = valueStr.charAt(pos++)) != ' '))
385 {
386 if (isAlpha(c) || isDigit(c) || (c == '-') ||
387 ((c == '_') && DirectoryServer.allowAttributeNameExceptions()))
388 {
389 // This is fine. It is an acceptable character.
390 }
391 else
392 {
393 // This must have been an illegal character.
394 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR_IN_STRING_OID.
395 get(valueStr, String.valueOf(c), (pos-1));
396 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
397 message);
398 }
399 }
400 }
401
402
403 // If we're at the end of the value, then it isn't a valid attribute type
404 // description. Otherwise, parse out the OID.
405 String oid;
406 if (pos >= length)
407 {
408 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_TRUNCATED_VALUE.get(valueStr);
409 throw new DirectoryException(
410 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
411 }
412 else
413 {
414 oid = lowerStr.substring(oidStartPos, (pos-1));
415 }
416
417
418 // Skip over the space(s) after the OID.
419 while ((pos < length) && ((c = valueStr.charAt(pos)) == ' '))
420 {
421 pos++;
422 }
423
424 if (pos >= length)
425 {
426 // This means that the end of the value was reached before we could find
427 // the OID. Ths is illegal.
428 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_TRUNCATED_VALUE.get(valueStr);
429 throw new DirectoryException(
430 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
431 }
432
433
434 // At this point, we should have a pretty specific syntax that describes
435 // what may come next, but some of the components are optional and it would
436 // be pretty easy to put something in the wrong order, so we will be very
437 // flexible about what we can accept. Just look at the next token, figure
438 // out what it is and how to treat what comes after it, then repeat until
439 // we get to the end of the value. But before we start, set default values
440 // for everything else we might need to know.
441 String primaryName = oid;
442 List<String> typeNames = new LinkedList<String>();
443 String description = null;
444 AttributeType superiorType = null;
445 AttributeSyntax syntax = DirectoryServer.getDefaultAttributeSyntax();
446 ApproximateMatchingRule approximateMatchingRule = null;
447 EqualityMatchingRule equalityMatchingRule = null;
448 OrderingMatchingRule orderingMatchingRule = null;
449 SubstringMatchingRule substringMatchingRule = null;
450 AttributeUsage attributeUsage = AttributeUsage.USER_APPLICATIONS;
451 boolean isCollective = false;
452 boolean isNoUserModification = false;
453 boolean isObsolete = false;
454 boolean isSingleValue = false;
455 HashMap<String,List<String>> extraProperties =
456 new LinkedHashMap<String,List<String>>();
457
458
459 while (true)
460 {
461 StringBuilder tokenNameBuffer = new StringBuilder();
462 pos = readTokenName(valueStr, tokenNameBuffer, pos);
463 String tokenName = tokenNameBuffer.toString();
464 String lowerTokenName = toLowerCase(tokenName);
465 if (tokenName.equals(")"))
466 {
467 // We must be at the end of the value. If not, then that's a problem.
468 if (pos < length)
469 {
470 Message message =
471 ERR_ATTR_SYNTAX_ATTRTYPE_UNEXPECTED_CLOSE_PARENTHESIS.
472 get(valueStr, (pos-1));
473 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
474 message);
475 }
476
477 break;
478 }
479 else if (lowerTokenName.equals("name"))
480 {
481 // This specifies the set of names for the attribute type. It may be a
482 // single name in single quotes, or it may be an open parenthesis
483 // followed by one or more names in single quotes separated by spaces.
484 c = valueStr.charAt(pos++);
485 if (c == '\'')
486 {
487 StringBuilder userBuffer = new StringBuilder();
488 StringBuilder lowerBuffer = new StringBuilder();
489 pos = readQuotedString(valueStr, lowerStr, userBuffer, lowerBuffer,
490 (pos-1));
491 primaryName = userBuffer.toString();
492 typeNames.add(primaryName);
493 }
494 else if (c == '(')
495 {
496 StringBuilder userBuffer = new StringBuilder();
497 StringBuilder lowerBuffer = new StringBuilder();
498 pos = readQuotedString(valueStr, lowerStr, userBuffer, lowerBuffer,
499 pos);
500 primaryName = userBuffer.toString();
501 typeNames.add(primaryName);
502
503
504 while (true)
505 {
506 if (valueStr.charAt(pos) == ')')
507 {
508 // Skip over any spaces after the parenthesis.
509 pos++;
510 while ((pos < length) && ((c = valueStr.charAt(pos)) == ' '))
511 {
512 pos++;
513 }
514
515 break;
516 }
517 else
518 {
519 userBuffer = new StringBuilder();
520 lowerBuffer = new StringBuilder();
521
522 pos = readQuotedString(valueStr, lowerStr, userBuffer,
523 lowerBuffer, pos);
524 typeNames.add(userBuffer.toString());
525 }
526 }
527 }
528 else
529 {
530 // This is an illegal character.
531 Message message =
532 ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR.get(
533 valueStr, String.valueOf(c), (pos-1));
534 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
535 message);
536 }
537 }
538 else if (lowerTokenName.equals("desc"))
539 {
540 // This specifies the description for the attribute type. It is an
541 // arbitrary string of characters enclosed in single quotes.
542 StringBuilder descriptionBuffer = new StringBuilder();
543 pos = readQuotedString(valueStr, descriptionBuffer, pos);
544 description = descriptionBuffer.toString();
545 }
546 else if (lowerTokenName.equals("obsolete"))
547 {
548 // This indicates whether the attribute type should be considered
549 // obsolete. We do not need to do any more parsing for this token.
550 isObsolete = true;
551 }
552 else if (lowerTokenName.equals("sup"))
553 {
554 // This specifies the name or OID of the superior attribute type from
555 // which this attribute type should inherit its properties.
556 StringBuilder woidBuffer = new StringBuilder();
557 pos = readWOID(lowerStr, woidBuffer, pos);
558 superiorType = schema.getAttributeType(woidBuffer.toString());
559 if (superiorType == null)
560 {
561 if (allowUnknownElements)
562 {
563 superiorType = DirectoryServer.getDefaultAttributeType(
564 woidBuffer.toString());
565 }
566 else
567 {
568 // This is bad because we don't know what the superior attribute
569 // type is so we can't base this attribute type on it.
570 Message message = WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_SUPERIOR_TYPE.
571 get(String.valueOf(oid), String.valueOf(woidBuffer));
572 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
573 message);
574 }
575 }
576
577
578 // Use the information in the superior type to provide defaults for the
579 // rest of the components in this attribute type description.
580 // Technically, the definition of the superior type should be provided
581 // before the matching rule, syntax, single-value, collective,
582 // no-user-modification, and usage components, and in that case we won't
583 // undo something else that has already been set by an earlier
584 // definition. However, if the information is provided out-of-order,
585 // then it is possible that this could overwrite some desired setting
586 // that is different from that of the supertype.
587 approximateMatchingRule = superiorType.getApproximateMatchingRule();
588 equalityMatchingRule = superiorType.getEqualityMatchingRule();
589 orderingMatchingRule = superiorType.getOrderingMatchingRule();
590 substringMatchingRule = superiorType.getSubstringMatchingRule();
591 syntax = superiorType.getSyntax();
592 isSingleValue = superiorType.isSingleValue();
593 isCollective = superiorType.isCollective();
594 isNoUserModification = superiorType.isNoUserModification();
595 attributeUsage = superiorType.getUsage();
596 }
597 else if (lowerTokenName.equals("equality"))
598 {
599 // This specifies the name or OID of the equality matching rule to use
600 // for this attribute type.
601 StringBuilder woidBuffer = new StringBuilder();
602 pos = readWOID(lowerStr, woidBuffer, pos);
603 EqualityMatchingRule emr =
604 schema.getEqualityMatchingRule(woidBuffer.toString());
605 if (emr == null)
606 {
607 // This is bad because we have no idea what the equality matching
608 // rule should be.
609 Message message = WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_EQUALITY_MR.get(
610 String.valueOf(oid), String.valueOf(woidBuffer));
611 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
612 message);
613 }
614 else
615 {
616 equalityMatchingRule = emr;
617 }
618 }
619 else if (lowerTokenName.equals("ordering"))
620 {
621 // This specifies the name or OID of the ordering matching rule to use
622 // for this attribute type.
623 StringBuilder woidBuffer = new StringBuilder();
624 pos = readWOID(lowerStr, woidBuffer, pos);
625 OrderingMatchingRule omr =
626 schema.getOrderingMatchingRule(woidBuffer.toString());
627 if (omr == null)
628 {
629 // This is bad because we have no idea what the ordering matching
630 // rule should be.
631 Message message = WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_ORDERING_MR.get(
632 String.valueOf(oid), String.valueOf(woidBuffer));
633 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
634 message);
635 }
636 else
637 {
638 orderingMatchingRule = omr;
639 }
640 }
641 else if (lowerTokenName.equals("substr"))
642 {
643 // This specifies the name or OID of the substring matching rule to use
644 // for this attribute type.
645 StringBuilder woidBuffer = new StringBuilder();
646 pos = readWOID(lowerStr, woidBuffer, pos);
647 SubstringMatchingRule smr =
648 schema.getSubstringMatchingRule(woidBuffer.toString());
649 if (smr == null)
650 {
651 // This is bad because we have no idea what the substring matching
652 // rule should be.
653 Message message = WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_SUBSTRING_MR.get(
654 String.valueOf(oid), String.valueOf(woidBuffer));
655 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
656 message);
657 }
658 else
659 {
660 substringMatchingRule = smr;
661 }
662 }
663 else if (lowerTokenName.equals("syntax"))
664 {
665 // This specifies the numeric OID of the syntax for this matching rule.
666 // It may optionally be immediately followed by an open curly brace, an
667 // integer value, and a close curly brace to suggest the minimum number
668 // of characters that should be allowed in values of that type. This
669 // implementation will ignore any such length because it does not
670 // impose any practical limit on the length of attribute values.
671 boolean inBrace = false;
672 boolean lastWasPeriod = false;
673 StringBuilder oidBuffer = new StringBuilder();
674 while (pos < length)
675 {
676 c = lowerStr.charAt(pos++);
677 if (inBrace)
678 {
679 // The only thing we'll allow here will be numeric digits and the
680 // closing curly brace.
681 if (c == '}')
682 {
683 // The next character must be a space.
684 if ((c = lowerStr.charAt(pos)) != ' ')
685 {
686 Message message =
687 ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR_IN_NUMERIC_OID.
688 get(valueStr, String.valueOf(c), (pos-1));
689 throw new DirectoryException(
690 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
691 }
692
693 break;
694 }
695 else if (! isDigit(c))
696 {
697 Message message =
698 ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR_IN_NUMERIC_OID.
699 get(valueStr, String.valueOf(c), (pos-1));
700 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
701 message);
702 }
703 }
704 else
705 {
706 if (isDigit(c))
707 {
708 oidBuffer.append(c);
709 lastWasPeriod = false;
710 }
711 else if (c == '.')
712 {
713 if (lastWasPeriod)
714 {
715 Message message =
716 ERR_ATTR_SYNTAX_ATTRTYPE_DOUBLE_PERIOD_IN_NUMERIC_OID.
717 get(valueStr, (pos-1));
718 throw new DirectoryException(
719 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
720 }
721 else
722 {
723 oidBuffer.append(c);
724 lastWasPeriod = true;
725 }
726 }
727 else if (c == '{')
728 {
729 // It's the start of the length specification.
730 inBrace = true;
731 }
732 else if (c == ' ')
733 {
734 // It's the end of the value.
735 break;
736 }
737 else
738 {
739 Message message =
740 ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR_IN_NUMERIC_OID.
741 get(valueStr, String.valueOf(c), (pos-1));
742 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
743 message);
744 }
745 }
746 }
747
748 syntax = schema.getSyntax(oidBuffer.toString());
749 if (syntax == null)
750 {
751 Message message = WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_SYNTAX.get(
752 String.valueOf(oid), String.valueOf(oidBuffer));
753 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION,
754 message);
755 }
756
757 if (approximateMatchingRule == null)
758 {
759 approximateMatchingRule = syntax.getApproximateMatchingRule();
760 }
761
762 if (equalityMatchingRule == null)
763 {
764 equalityMatchingRule = syntax.getEqualityMatchingRule();
765 }
766
767 if (orderingMatchingRule == null)
768 {
769 orderingMatchingRule = syntax.getOrderingMatchingRule();
770 }
771
772 if (substringMatchingRule == null)
773 {
774 substringMatchingRule = syntax.getSubstringMatchingRule();
775 }
776 }
777 else if (lowerTokenName.equals("single-value"))
778 {
779 // This indicates that attributes of this type are allowed to have at
780 // most one value. We do not need any more parsing for this token.
781 isSingleValue = true;
782 }
783 else if (lowerTokenName.equals("collective"))
784 {
785 // This indicates that attributes of this type are collective (i.e.,
786 // have their values generated dynamically in some way). We do not need
787 // any more parsing for this token.
788 isCollective = true;
789 }
790 else if (lowerTokenName.equals("no-user-modification"))
791 {
792 // This indicates that the values of attributes of this type are not to
793 // be modified by end users. We do not need any more parsing for this
794 // token.
795 isNoUserModification = true;
796 }
797 else if (lowerTokenName.equals("usage"))
798 {
799 // This specifies the usage string for this attribute type. It should
800 // be followed by one of the strings "userApplications",
801 // "directoryOperation", "distributedOperation", or "dSAOperation".
802 StringBuilder usageBuffer = new StringBuilder();
803 while (pos < length)
804 {
805 c = lowerStr.charAt(pos++);
806 if (c == ' ')
807 {
808 break;
809 }
810 else
811 {
812 usageBuffer.append(c);
813 }
814 }
815
816 String usageStr = usageBuffer.toString();
817 if (usageStr.equals("userapplications"))
818 {
819 attributeUsage = AttributeUsage.USER_APPLICATIONS;
820 }
821 else if (usageStr.equals("directoryoperation"))
822 {
823 attributeUsage = AttributeUsage.DIRECTORY_OPERATION;
824 }
825 else if (usageStr.equals("distributedoperation"))
826 {
827 attributeUsage = AttributeUsage.DISTRIBUTED_OPERATION;
828 }
829 else if (usageStr.equals("dsaoperation"))
830 {
831 attributeUsage = AttributeUsage.DSA_OPERATION;
832 }
833 else
834 {
835 // This must be an illegal usage.
836 attributeUsage = AttributeUsage.USER_APPLICATIONS;
837
838 Message message = WARN_ATTR_SYNTAX_ATTRTYPE_INVALID_ATTRIBUTE_USAGE.
839 get(String.valueOf(oid), usageStr);
840 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
841 message);
842 }
843 }
844 else
845 {
846 // This must be a non-standard property and it must be followed by
847 // either a single value in single quotes or an open parenthesis
848 // followed by one or more values in single quotes separated by spaces
849 // followed by a close parenthesis.
850 List<String> valueList = new ArrayList<String>();
851 pos = readExtraParameterValues(valueStr, valueList, pos);
852 extraProperties.put(tokenName, valueList);
853 }
854 }
855
856 List<String> approxRules = extraProperties.get(SCHEMA_PROPERTY_APPROX_RULE);
857 if ((approxRules != null) && (! approxRules.isEmpty()))
858 {
859 String ruleName = approxRules.get(0);
860 String lowerName = toLowerCase(ruleName);
861 ApproximateMatchingRule amr =
862 schema.getApproximateMatchingRule(lowerName);
863 if (amr == null)
864 {
865 // This is bad because we have no idea what the approximate matching
866 // rule should be.
867 Message message = WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_APPROXIMATE_MR.get(
868 String.valueOf(oid), String.valueOf(ruleName));
869 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
870 }
871 else
872 {
873 approximateMatchingRule = amr;
874 }
875 }
876
877
878 // If there is a superior type, then it must have the same usage as the
879 // subordinate type. Also, if the superior type is collective, then so must
880 // the subordinate type be collective.
881 if (superiorType != null)
882 {
883 if (superiorType.getUsage() != attributeUsage)
884 {
885 Message message = WARN_ATTR_SYNTAX_ATTRTYPE_INVALID_SUPERIOR_USAGE.get(
886 oid, String.valueOf(attributeUsage), superiorType.getNameOrOID());
887 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
888 }
889
890 if (superiorType.isCollective() != isCollective)
891 {
892 Message message;
893 if (isCollective)
894 {
895 message = WARN_ATTR_SYNTAX_ATTRTYPE_COLLECTIVE_FROM_NONCOLLECTIVE.get(
896 oid, superiorType.getNameOrOID());
897 }
898 else
899 {
900 message =
901 WARN_ATTR_SYNTAX_ATTRTYPE_NONCOLLECTIVE_FROM_COLLECTIVE.get(
902 oid, superiorType.getNameOrOID());
903 }
904 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
905 }
906 }
907
908
909 // If the attribute type is COLLECTIVE, then it must have a usage of
910 // userApplications.
911 if (isCollective && (attributeUsage != AttributeUsage.USER_APPLICATIONS))
912 {
913 Message message =
914 WARN_ATTR_SYNTAX_ATTRTYPE_COLLECTIVE_IS_OPERATIONAL.get(oid);
915 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
916 }
917
918
919 // If the attribute type is NO-USER-MODIFICATION, then it must not have a
920 // usage of userApplications.
921 if (isNoUserModification &&
922 (attributeUsage == AttributeUsage.USER_APPLICATIONS))
923 {
924 Message message =
925 WARN_ATTR_SYNTAX_ATTRTYPE_NO_USER_MOD_NOT_OPERATIONAL.get(oid);
926 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message);
927 }
928
929
930 return new AttributeType(value.stringValue(), primaryName, typeNames, oid,
931 description, superiorType, syntax,
932 approximateMatchingRule, equalityMatchingRule,
933 orderingMatchingRule, substringMatchingRule,
934 attributeUsage, isCollective, isNoUserModification,
935 isObsolete, isSingleValue, extraProperties);
936 }
937
938
939
940 /**
941 * Reads the next token name from the attribute type definition, skipping over
942 * any leading or trailing spaces, and appends it to the provided buffer.
943 *
944 * @param valueStr The string representation of the attribute type
945 * definition.
946 * @param tokenName The buffer into which the token name will be written.
947 * @param startPos The position in the provided string at which to start
948 * reading the token name.
949 *
950 * @return The position of the first character that is not part of the token
951 * name or one of the trailing spaces after it.
952 *
953 * @throws DirectoryException If a problem is encountered while reading the
954 * token name.
955 */
956 private static int readTokenName(String valueStr, StringBuilder tokenName,
957 int startPos)
958 throws DirectoryException
959 {
960 // Skip over any spaces at the beginning of the value.
961 char c = '\u0000';
962 int length = valueStr.length();
963 while ((startPos < length) && ((c = valueStr.charAt(startPos)) == ' '))
964 {
965 startPos++;
966 }
967
968 if (startPos >= length)
969 {
970 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_TRUNCATED_VALUE.get(valueStr);
971 throw new DirectoryException(
972 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
973 }
974
975
976 // Read until we find the next space.
977 while ((startPos < length) && ((c = valueStr.charAt(startPos++)) != ' '))
978 {
979 tokenName.append(c);
980 }
981
982
983 // Skip over any trailing spaces after the value.
984 while ((startPos < length) && ((c = valueStr.charAt(startPos)) == ' '))
985 {
986 startPos++;
987 }
988
989
990 // Return the position of the first non-space character after the token.
991 return startPos;
992 }
993
994
995
996 /**
997 * Reads the value of a string enclosed in single quotes, skipping over the
998 * quotes and any leading or trailing spaces, and appending the string to the
999 * provided buffer.
1000 *
1001 * @param valueStr The user-provided representation of the attribute type
1002 * definition.
1003 * @param valueBuffer The buffer into which the user-provided representation
1004 * of the value will be placed.
1005 * @param startPos The position in the provided string at which to start
1006 * reading the quoted string.
1007 *
1008 * @return The position of the first character that is not part of the quoted
1009 * string or one of the trailing spaces after it.
1010 *
1011 * @throws DirectoryException If a problem is encountered while reading the
1012 * quoted string.
1013 */
1014 private static int readQuotedString(String valueStr,
1015 StringBuilder valueBuffer, int startPos)
1016 throws DirectoryException
1017 {
1018 // Skip over any spaces at the beginning of the value.
1019 char c = '\u0000';
1020 int length = valueStr.length();
1021 while ((startPos < length) && ((c = valueStr.charAt(startPos)) == ' '))
1022 {
1023 startPos++;
1024 }
1025
1026 if (startPos >= length)
1027 {
1028 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_TRUNCATED_VALUE.get(valueStr);
1029 throw new DirectoryException(
1030 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
1031 }
1032
1033
1034 // The next character must be a single quote.
1035 if (c != '\'')
1036 {
1037 Message message = WARN_ATTR_SYNTAX_ATTRTYPE_EXPECTED_QUOTE_AT_POS.get(
1038 valueStr, startPos, String.valueOf(c));
1039 throw new DirectoryException(
1040 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
1041 }
1042
1043
1044 // Read until we find the closing quote.
1045 startPos++;
1046 while ((startPos < length) && ((c = valueStr.charAt(startPos)) != '\''))
1047 {
1048 valueBuffer.append(c);
1049 startPos++;
1050 }
1051
1052
1053 // Skip over any trailing spaces after the value.
1054 startPos++;
1055 while ((startPos < length) && ((c = valueStr.charAt(startPos)) == ' '))
1056 {
1057 startPos++;
1058 }
1059
1060
1061 // If we're at the end of the value, then that's illegal.
1062 if (startPos >= length)
1063 {
1064 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_TRUNCATED_VALUE.get(valueStr);
1065 throw new DirectoryException(
1066 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
1067 }
1068
1069
1070 // Return the position of the first non-space character after the token.
1071 return startPos;
1072 }
1073
1074
1075
1076 /**
1077 * Reads the value of a string enclosed in single quotes, skipping over the
1078 * quotes and any leading or trailing spaces, and appending the string to the
1079 * provided buffer.
1080 *
1081 * @param valueStr The user-provided representation of the attribute type
1082 * definition.
1083 * @param lowerStr The all-lowercase representation of the attribute type
1084 * definition.
1085 * @param userBuffer The buffer into which the user-provided representation
1086 * of the value will be placed.
1087 * @param lowerBuffer The buffer into which the all-lowercase representation
1088 * of the value will be placed.
1089 * @param startPos The position in the provided string at which to start
1090 * reading the quoted string.
1091 *
1092 * @return The position of the first character that is not part of the quoted
1093 * string or one of the trailing spaces after it.
1094 *
1095 * @throws DirectoryException If a problem is encountered while reading the
1096 * quoted string.
1097 */
1098 private static int readQuotedString(String valueStr, String lowerStr,
1099 StringBuilder userBuffer,
1100 StringBuilder lowerBuffer, int startPos)
1101 throws DirectoryException
1102 {
1103 // Skip over any spaces at the beginning of the value.
1104 char c = '\u0000';
1105 int length = lowerStr.length();
1106 while ((startPos < length) && ((c = lowerStr.charAt(startPos)) == ' '))
1107 {
1108 startPos++;
1109 }
1110
1111 if (startPos >= length)
1112 {
1113 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_TRUNCATED_VALUE.get(lowerStr);
1114 throw new DirectoryException(
1115 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
1116 }
1117
1118
1119 // The next character must be a single quote.
1120 if (c != '\'')
1121 {
1122 Message message = WARN_ATTR_SYNTAX_ATTRTYPE_EXPECTED_QUOTE_AT_POS.get(
1123 valueStr, startPos, String.valueOf(c));
1124 throw new DirectoryException(
1125 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
1126 }
1127
1128
1129 // Read until we find the closing quote.
1130 startPos++;
1131 while ((startPos < length) && ((c = lowerStr.charAt(startPos)) != '\''))
1132 {
1133 lowerBuffer.append(c);
1134 userBuffer.append(valueStr.charAt(startPos));
1135 startPos++;
1136 }
1137
1138
1139 // Skip over any trailing spaces after the value.
1140 startPos++;
1141 while ((startPos < length) && ((c = lowerStr.charAt(startPos)) == ' '))
1142 {
1143 startPos++;
1144 }
1145
1146
1147 // If we're at the end of the value, then that's illegal.
1148 if (startPos >= length)
1149 {
1150 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_TRUNCATED_VALUE.get(lowerStr);
1151 throw new DirectoryException(
1152 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
1153 }
1154
1155
1156 // Return the position of the first non-space character after the token.
1157 return startPos;
1158 }
1159
1160
1161
1162 /**
1163 * Reads the attribute description or numeric OID from the provided string,
1164 * skipping over any leading or trailing spaces, and appending the value to
1165 * the provided buffer.
1166 *
1167 * @param lowerStr The string from which the name or OID is to be read.
1168 * @param woidBuffer The buffer into which the name or OID should be
1169 * appended.
1170 * @param startPos The position at which to start reading.
1171 *
1172 * @return The position of the first character after the name or OID that is
1173 * not a space.
1174 *
1175 * @throws DirectoryException If a problem is encountered while reading the
1176 * name or OID.
1177 */
1178 private static int readWOID(String lowerStr, StringBuilder woidBuffer,
1179 int startPos)
1180 throws DirectoryException
1181 {
1182 // Skip over any spaces at the beginning of the value.
1183 char c = '\u0000';
1184 int length = lowerStr.length();
1185 while ((startPos < length) && ((c = lowerStr.charAt(startPos)) == ' '))
1186 {
1187 startPos++;
1188 }
1189
1190 if (startPos >= length)
1191 {
1192 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_TRUNCATED_VALUE.get(lowerStr);
1193 throw new DirectoryException(
1194 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
1195 }
1196
1197
1198 // The next character must be either numeric (for an OID) or alphabetic (for
1199 // an attribute description).
1200 if (isDigit(c))
1201 {
1202 // This must be a numeric OID. In that case, we will accept only digits
1203 // and periods, but not consecutive periods.
1204 boolean lastWasPeriod = false;
1205 while ((startPos < length) && ((c = lowerStr.charAt(startPos++)) != ' '))
1206 {
1207 if (c == '.')
1208 {
1209 if (lastWasPeriod)
1210 {
1211 Message message =
1212 ERR_ATTR_SYNTAX_ATTRTYPE_DOUBLE_PERIOD_IN_NUMERIC_OID.
1213 get(lowerStr, (startPos-1));
1214 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
1215 message);
1216 }
1217 else
1218 {
1219 woidBuffer.append(c);
1220 lastWasPeriod = true;
1221 }
1222 }
1223 else if (! isDigit(c))
1224 {
1225 // Technically, this must be an illegal character. However, it is
1226 // possible that someone just got sloppy and did not include a space
1227 // between the name/OID and a closing parenthesis. In that case,
1228 // we'll assume it's the end of the value. What's more, we'll have
1229 // to prematurely return to nasty side effects from stripping off
1230 // additional characters.
1231 if (c == ')')
1232 {
1233 return (startPos-1);
1234 }
1235
1236 // This must have been an illegal character.
1237 Message message =
1238 ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR_IN_NUMERIC_OID.
1239 get(lowerStr, String.valueOf(c), (startPos-1));
1240 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
1241 message);
1242 }
1243 else
1244 {
1245 woidBuffer.append(c);
1246 lastWasPeriod = false;
1247 }
1248 }
1249 }
1250 else if (isAlpha(c))
1251 {
1252 // This must be an attribute description. In this case, we will only
1253 // accept alphabetic characters, numeric digits, and the hyphen.
1254 while ((startPos < length) && ((c = lowerStr.charAt(startPos++)) != ' '))
1255 {
1256 if (isAlpha(c) || isDigit(c) || (c == '-') ||
1257 ((c == '_') && DirectoryServer.allowAttributeNameExceptions()))
1258 {
1259 woidBuffer.append(c);
1260 }
1261 else
1262 {
1263 // Technically, this must be an illegal character. However, it is
1264 // possible that someone just got sloppy and did not include a space
1265 // between the name/OID and a closing parenthesis. In that case,
1266 // we'll assume it's the end of the value. What's more, we'll have
1267 // to prematurely return to nasty side effects from stripping off
1268 // additional characters.
1269 if (c == ')')
1270 {
1271 return (startPos-1);
1272 }
1273
1274 // This must have been an illegal character.
1275 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR_IN_STRING_OID.
1276 get(lowerStr, String.valueOf(c), (startPos-1));
1277 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
1278 message);
1279 }
1280 }
1281 }
1282 else
1283 {
1284 Message message =
1285 ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR.
1286 get(lowerStr, String.valueOf(c), startPos);
1287 throw new DirectoryException(
1288 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
1289 }
1290
1291
1292 // Skip over any trailing spaces after the value.
1293 while ((startPos < length) && ((c = lowerStr.charAt(startPos)) == ' '))
1294 {
1295 startPos++;
1296 }
1297
1298
1299 // If we're at the end of the value, then that's illegal.
1300 if (startPos >= length)
1301 {
1302 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_TRUNCATED_VALUE.get(lowerStr);
1303 throw new DirectoryException(
1304 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
1305 }
1306
1307
1308 // Return the position of the first non-space character after the token.
1309 return startPos;
1310 }
1311
1312
1313
1314 /**
1315 * Reads the value for an "extra" parameter. It will handle a single unquoted
1316 * word (which is technically illegal, but we'll allow it), a single quoted
1317 * string, or an open parenthesis followed by a space-delimited set of quoted
1318 * strings or unquoted words followed by a close parenthesis.
1319 *
1320 * @param valueStr The string containing the information to be read.
1321 * @param valueList The list of "extra" parameter values read so far.
1322 * @param startPos The position in the value string at which to start
1323 * reading.
1324 *
1325 * @return The "extra" parameter value that was read.
1326 *
1327 * @throws DirectoryException If a problem occurs while attempting to read
1328 * the value.
1329 */
1330 private static int readExtraParameterValues(String valueStr,
1331 List<String> valueList, int startPos)
1332 throws DirectoryException
1333 {
1334 // Skip over any leading spaces.
1335 int length = valueStr.length();
1336 char c = valueStr.charAt(startPos++);
1337 while ((startPos < length) && (c == ' '))
1338 {
1339 c = valueStr.charAt(startPos++);
1340 }
1341
1342 if (startPos >= length)
1343 {
1344 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_TRUNCATED_VALUE.get(valueStr);
1345 throw new DirectoryException(
1346 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
1347 }
1348
1349
1350 // Look at the next character. If it is a quote, then parse until the next
1351 // quote and end. If it is an open parenthesis, then parse individual
1352 // values until the close parenthesis and end. Otherwise, parse until the
1353 // next space and end.
1354 if (c == '\'')
1355 {
1356 // Parse until the closing quote.
1357 StringBuilder valueBuffer = new StringBuilder();
1358 while ((startPos < length) && ((c = valueStr.charAt(startPos++)) != '\''))
1359 {
1360 valueBuffer.append(c);
1361 }
1362
1363 valueList.add(valueBuffer.toString());
1364 }
1365 else if (c == '(')
1366 {
1367 while (true)
1368 {
1369 // Skip over any leading spaces;
1370 startPos++;
1371 while ((startPos < length) && ((c = valueStr.charAt(startPos)) == ' '))
1372 {
1373 startPos++;
1374 }
1375
1376 if (startPos >= length)
1377 {
1378 Message message =
1379 ERR_ATTR_SYNTAX_ATTRTYPE_TRUNCATED_VALUE.get(valueStr);
1380 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
1381 message);
1382 }
1383
1384
1385 if (c == ')')
1386 {
1387 // This is the end of the list.
1388 break;
1389 }
1390 else if (c == '(')
1391 {
1392 // This is an illegal character.
1393 Message message =
1394 ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_CHAR.get(
1395 valueStr, String.valueOf(c), startPos);
1396 throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
1397 message);
1398 }
1399 else
1400 {
1401 // We'll recursively call this method to deal with this.
1402 startPos = readExtraParameterValues(valueStr, valueList, startPos);
1403 }
1404 }
1405 }
1406 else
1407 {
1408 // Parse until the next space.
1409 StringBuilder valueBuffer = new StringBuilder();
1410 while ((startPos < length) && ((c = valueStr.charAt(startPos++)) != ' '))
1411 {
1412 valueBuffer.append(c);
1413 }
1414
1415 valueList.add(valueBuffer.toString());
1416 }
1417
1418
1419
1420 // Skip over any trailing spaces.
1421 while ((startPos < length) && (valueStr.charAt(startPos) == ' '))
1422 {
1423 startPos++;
1424 }
1425
1426 if (startPos >= length)
1427 {
1428 Message message = ERR_ATTR_SYNTAX_ATTRTYPE_TRUNCATED_VALUE.get(valueStr);
1429 throw new DirectoryException(
1430 ResultCode.INVALID_ATTRIBUTE_SYNTAX, message);
1431 }
1432
1433
1434 return startPos;
1435 }
1436
1437
1438
1439 /**
1440 * {@inheritDoc}
1441 */
1442 public ConfigChangeResult applyConfigurationChange(
1443 AttributeTypeDescriptionAttributeSyntaxCfg configuration)
1444 {
1445 currentConfig = configuration;
1446 stripMinimumUpperBound = configuration.isStripSyntaxMinUpperBound();
1447
1448 return new ConfigChangeResult(ResultCode.SUCCESS, false);
1449 }
1450
1451
1452
1453 /**
1454 * {@inheritDoc}
1455 */
1456 public boolean isConfigurationChangeAcceptable(
1457 AttributeTypeDescriptionAttributeSyntaxCfg configuration,
1458 List<Message> unacceptableReasons)
1459 {
1460 // The configuration will always be acceptable.
1461 return true;
1462 }
1463
1464 /**
1465 * Boolean that indicates that the minimum upper bound value should be
1466 * stripped from the Attrbute Type Syntax Description.
1467 *
1468 * @return True if the minimum upper bound value should be stripped.
1469 */
1470 public static boolean isStripSyntaxMinimumUpperBound() {
1471 return stripMinimumUpperBound;
1472 }
1473
1474 }
1475