001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: AttributeSchema.java,v 1.13 2009/01/13 06:56:08 mahesh_prasad_r Exp $
026 *
027 */
028
029package com.sun.identity.sm;
030
031import java.security.AccessController;
032import java.text.MessageFormat;
033import java.util.Collections;
034import java.util.HashMap;
035import java.util.HashSet;
036import java.util.Iterator;
037import java.util.Map;
038import java.util.Set;
039
040import org.w3c.dom.Document;
041import org.w3c.dom.Element;
042import org.w3c.dom.Node;
043
044import com.sun.identity.shared.debug.Debug;
045import com.sun.identity.shared.xml.XMLUtils;
046import com.iplanet.sso.SSOException;
047import com.sun.identity.security.EncodeAction;
048
049/**
050 * The class <code>AttributeSchema</code> provides methods to access the
051 * schema of a configuration parameter. Also, it provides methods to set default
052 * and choice values.
053 *
054 * @supported.all.api
055 */
056public class AttributeSchema {
057    // Debug
058    private static Debug debug = SMSEntry.debug;
059
060    // Instance variable
061    ServiceSchemaManager ssm;
062
063    ServiceSchema ss;
064
065    PluginSchema ps;
066
067    AttributeSchemaImpl as;
068
069    /**
070     * Constructor. Makes it private so that it cannot be instantiated.
071     */
072    private AttributeSchema() {
073    }
074
075    /**
076     * Constructor used by ServiceSchema to instantiate
077     * <code>AttributeSchema</code> objects.
078     */
079    protected AttributeSchema(AttributeSchemaImpl as, ServiceSchemaManager ssm,
080            ServiceSchema ss) {
081        this.ssm = ssm;
082        this.ss = ss;
083        this.as = as;
084        if (as == null) {
085            debug.error("AttributeSchema:: IMPL is NULL");
086        }
087    }
088
089    protected AttributeSchema(AttributeSchemaImpl as, PluginSchema ps) {
090        this.as = as;
091        this.ps = ps;
092    }
093
094    /**
095     * Returns the name of the attribute.
096     * 
097     * @return the name of the attribute
098     */
099    public String getName() {
100        return (as.getName());
101    }
102
103    /**
104     * Returns the type of the attribute.
105     * 
106     * @return the type of the attribute
107     */
108    public AttributeSchema.Type getType() {
109        return (as.getType());
110    }
111
112    /**
113     * Returns Service Schema.
114     * 
115     * @return Service Schema.
116     */
117    public ServiceSchema getServiceSchema() {
118        return ss;
119    }
120
121    /**
122     * Sets the type.
123     * 
124     * @param type
125     *            to be changed to
126     * @throws SMSException
127     *             if an error is encountered when trying to set
128     * @throws SSOException
129     *             if the single sign on token is invalid or expired
130     */
131    public void setType(String type) throws SMSException, SSOException {
132        updateXMLDocument(SMSUtils.ATTRIBUTE_TYPE, type);
133    }
134
135    /**
136     * Returns the UI type of the attribute.
137     * 
138     * @return the UI type of the attribute; or null if the UI Type is not
139     *         defined
140     */
141    public AttributeSchema.UIType getUIType() {
142        return (as.getUIType());
143    }
144
145    /**
146     * Sets the <code>UIType</code> attribute.
147     * 
148     * @param uiType
149     *            user interface type.
150     * @throws SMSException
151     *             if an error is encountered when trying to set
152     *             <code>UIType</code> to the attribute schema.
153     * @throws SSOException
154     *             if the single sign on token is invalid or expired
155     */
156    public void setUIType(String uiType) throws SMSException, SSOException {
157        updateXMLDocument(SMSUtils.ATTRIBUTE_UITYPE, uiType);
158    }
159
160    /**
161     * Returns the syntax of the attribute.
162     * 
163     * @return the syntax of the attribute
164     */
165    public AttributeSchema.Syntax getSyntax() {
166        return (as.getSyntax());
167    }
168
169    /**
170     * Sets the Syntax attribute.
171     * 
172     * @param synt
173     *            syntax
174     * @throws SMSException
175     *             if an error is encountered when trying to set the attribute
176     *             syntax
177     * @throws SSOException
178     *             if the single sign on token is invalid or expired
179     */
180    public void setSyntax(String synt) throws SMSException, SSOException {
181        updateXMLDocument(SMSUtils.ATTRIBUTE_SYNTAX, synt);
182    }
183
184    /**
185     * Returns the I18N key to describe the configuration attribute.
186     * 
187     * @return the I18N key to describe the configuration attribute
188     */
189    public String getI18NKey() {
190        return (as.getI18NKey());
191    }
192
193    /**
194     * Sets the I18N key to describe the configuration attribute.
195     * 
196     * @param i18nKey
197     *            the I18N key to describe the attribute
198     * @throws SMSException
199     *             if an error is encountered when trying to set I18N key to the
200     *             attribute schema
201     * @throws SSOException
202     *             if the single sign on token is invalid or expired
203     */
204    public void setI18NKey(String i18nKey) throws SMSException, SSOException {
205        updateXMLDocument(SMSUtils.I18N_KEY, i18nKey);
206    }
207
208    /**
209     * Returns the value of the <code>cosQualifier</code> for this attribute
210     * that is <code>default, override, operational or merge-cos</code>.
211     * 
212     * @return the value of the <code>cosQualifier</code>.
213     */
214    public String getCosQualifier() {
215        return (as.getCosQualifier());
216    }
217
218    /**
219     * Sets the <code>cosQualifier</code> attribute
220     * 
221     * @param cosq
222     *            value of <code>cosQualifier</code>.
223     * @throws SMSException
224     *             if an error is encountered when trying to set.
225     * @throws SSOException
226     *             if the single sign on token is invalid or expired
227     */
228    public void setCosQualifier(String cosq) throws SMSException, SSOException {
229        updateXMLDocument(SMSUtils.ATTRIBUTE_COS_QUALIFIER, cosq);
230    }
231
232    /**
233     * Returns the default values of the attribute. If there are no default
234     * values defined for this attribute in the schema then this method returns
235     * a Collections.EMPTY_SET
236     * 
237     * @return set of default values of the attribute
238     */
239    public Set getDefaultValues() {
240        return (as.getDefaultValues());
241    }
242
243    /**
244     * Returns the default values of the attribute for the given environment
245     * parameters. If there are no default values defined for this attribute in
246     * the schema then this method returns a Collections.EMPTY_SET
247     * 
248     * @param envParams
249     *            Map of environment parameter to a set of values
250     * @return set of default values of the attribute
251     */
252    public Set getDefaultValues(Map envParams) {
253        return (as.getDefaultValues(envParams));
254    }
255
256    /**
257     * Sets the default values of the attribute.
258     * 
259     * @param values
260     *            the set of default values
261     * @throws SMSException
262     *             if an error is encountered when trying to set.
263     * @throws SSOException
264     *             if the single sign on token is invalid or expired
265     */
266    public void setDefaultValues(Set values) throws SMSException, SSOException {
267        updateDefaultValues(values);
268    }
269
270    /**
271     * Protected method to set the default values in the given XML document.
272     * 
273     * @throws SMSException
274     *             if an error is encountered when trying to set.
275     * @throws SSOException
276     *             if the single sign on token is invalid or expired
277     */
278    void setDefaultValues(Set values, Document document) throws SMSException,
279            SSOException {
280        updateDefaultValues(values, document);
281    }
282
283    /**
284     * Adds a default value to the existing set of default values.
285     * 
286     * @param value
287     *            the default value to add
288     * @throws SMSException
289     *             if an error is encountered when trying to set.
290     * @throws SSOException
291     *             if the single sign on token is invalid or expired
292     */
293    public void addDefaultValue(String value) throws SMSException, SSOException
294    {
295        Set defaultValues = getDefaultValues();
296        if (defaultValues != Collections.EMPTY_SET) {
297            defaultValues.add(value);
298        } else {
299            defaultValues = new HashSet();
300            defaultValues.add(value);
301        }
302        updateDefaultValues(defaultValues);
303    }
304
305    /**
306     * Removes the all the default values for the attribute.
307     * 
308     * @throws SMSException
309     *             if an error is encountered when trying to set.
310     * @throws SSOException
311     *             if the single sign on token is invalid or expired
312     * 
313     */
314    public void removeDefaultValues() throws SMSException, SSOException {
315        updateDefaultValues(new HashSet());
316    }
317
318    /**
319     * Removes the given value from the set of default values.
320     * 
321     * @param value
322     *            the default value to remove
323     * @throws SMSException
324     *             if an error is encountered when trying to set.
325     * @throws SSOException
326     *             if the single sign on token is invalid or expired
327     */
328    public void removeDefaultValue(String value) throws SMSException,
329            SSOException {
330        Set defaultValues = getDefaultValues();
331        if (defaultValues != Collections.EMPTY_SET) {
332            defaultValues.remove(value);
333            updateDefaultValues(defaultValues);
334        }
335    }
336
337    /**
338     * Returns the possible choice values for the attribute if the attribute
339     * type is either <code>SINGLE_CHOICE</code> or
340     * <code>MULTIPLE_CHOICE</code>.
341     * 
342     * @return set of possible choice values
343     */
344    public String[] getChoiceValues() {
345        return (as.getChoiceValues());
346    }
347
348    /**
349     * Returns the possible choice values for the attribute if the attribute
350     * type is either <code>SINGLE_CHOICE</code> or
351     * <code>MULTIPLE_CHOICE</code>, for the given environment parameters.
352     * 
353     * @param envParams
354     *            Map of environment parameter to a set of values
355     * @return set of possible choice values
356     */
357    public String[] getChoiceValues(Map envParams) {
358        return (as.getChoiceValues(envParams));
359    }
360
361    /**
362     * Returns the I18N key for the given choice value.
363     * 
364     * @param cValue
365     *            choice value.
366     * @return the I18N key for the given choice value
367     */
368    public String getChoiceValueI18NKey(String cValue) {
369        return (as.getChoiceValueI18NKey(cValue));
370    }
371
372    /**
373     * Adds a choice value and its i18n key to the existing set of choice
374     * values.
375     * 
376     * @param value
377     *            the choice value to add
378     * @param i18nKey
379     *            the I18N key for the choice value
380     * @throws SMSException
381     *             if an error is encountered when trying to set.
382     * @throws SSOException
383     *             if the single sign on token is invalid or expired
384     */
385    public void addChoiceValue(String value, String i18nKey)
386            throws SMSException, SSOException {
387        Map choiceValues = as.getChoiceValuesMap();
388        choiceValues.put(value, i18nKey);
389        updateChoiceValues(choiceValues);
390    }
391
392    /**
393     * Removes the given value from the set of choice values.
394     * 
395     * @param value
396     *            the choice value to remove
397     * @throws SMSException
398     *             if an error is encountered when trying to set.
399     * @throws SSOException
400     *             if the single sign on token is invalid or expired
401     */
402    public void removeChoiceValue(String value) throws SMSException,
403            SSOException {
404        Map choiceValues = as.getChoiceValuesMap();
405        if (choiceValues.remove(value) != null) {
406            updateChoiceValues(choiceValues);
407        }
408    }
409
410    /**
411     * Returns the start range if the attribute syntax is either
412     * <code>NUMBER_RANGE</code> or <code>DECIMAL_RANGE</code>.
413     * 
414     * @return the start range for the attribute value
415     */
416    public String getStartRange() {
417        return (as.getStartRange());
418    }
419
420    /**
421     * Sets the start range attribute.
422     * 
423     * @param stRange
424     *            start range.
425     * @throws SMSException
426     *             if an error is encountered when trying to set
427     * @throws SSOException
428     *             if the single sign on token is invalid or expired
429     */
430    public void setStartRange(String stRange) throws SMSException, SSOException
431    {
432        updateXMLDocument(SMSUtils.ATTRIBUTE_RANGE_START, stRange);
433    }
434
435    /**
436     * Returns the end range if the attribute syntax is either
437     * <code>NUMBER_RANGE</code> or <code>DECIMAL_RANGE</code>.
438     * 
439     * @return the end range for the attribute value
440     */
441    public String getEndRange() {
442        return (as.getEndRange());
443    }
444
445    /**
446     * Sets the end range Attribute.
447     * 
448     * @param edRange
449     *            end range.
450     * @throws SMSException
451     *             if an error is encountered when trying to set
452     * @throws SSOException
453     *             if the single sign on token is invalid or expired
454     */
455    public void setEndRange(String edRange) throws SMSException, SSOException {
456        updateXMLDocument(SMSUtils.ATTRIBUTE_RANGE_END, edRange);
457    }
458
459    /**
460     * Method to get the validator name for using to validate this service
461     * attribute
462     * 
463     * @return the validator name
464     */
465    public String getValidator() {
466        return (as.getValidator());
467    }
468
469    /**
470     * Sets the Validator attribute
471     * 
472     * @param valid
473     *            validator
474     * @throws SMSException
475     *             if an error is encountered when trying to set
476     * @throws SSOException
477     *             if the single sign on token is invalid or expired
478     */
479    public void setValidator(String valid) throws SMSException, SSOException {
480        updateXMLDocument(SMSUtils.ATTRIBUTE_VALIDATOR, valid);
481    }
482
483    /**
484     * Returns the minimum number of values for the attribute if the attribute
485     * is of type <code>MULTIPLE_CHOICE</code>.
486     * 
487     * @return the minimum number of values
488     */
489    public int getMinValue() {
490        return (as.getMinValue());
491    }
492
493    /**
494     * Sets the minimum value attribute.
495     * 
496     * @param minV
497     *            minimum value.
498     * @throws SMSException
499     *             if an error is encountered when trying to set
500     * @throws SSOException
501     *             if the single sign on token is invalid or expired
502     */
503    public void setMinValue(String minV) throws SMSException, SSOException {
504        updateXMLDocument(SMSUtils.ATTRIBUTE_MIN_VALUE, minV);
505    }
506
507    /**
508     * Returns the maximum number of values for the attribute if the attribute
509     * is of type <code>MULTIPLE_CHOICE</code>.
510     * 
511     * @return the maximum number of values
512     */
513    public int getMaxValue() {
514        return (as.getMaxValue());
515    }
516
517    /**
518     * Sets the maximum value attribute.
519     * 
520     * @param maxV
521     *            maximum value.
522     * @throws SMSException
523     *             if an error is encountered when trying to set
524     * @throws SSOException
525     *             if the single sign on token is invalid or expired
526     */
527    public void setMaxValue(String maxV) throws SMSException, SSOException {
528        updateXMLDocument(SMSUtils.ATTRIBUTE_MAX_VALUE, maxV);
529    }
530
531    /**
532     * Sets the boolean values of the attribute.
533     *
534     * @param trueValue string value for <code>BooleanTrueValue</code>.
535     * @param trueValueI18nKey <code>I18N</code> key for
536     *        <code>BooleanTrueValue</code>.
537     * @param falseValue string value for <code>BooleanFalseValue</code>.
538     * @param falseValueI18nKey <code>I18N</code> Key for
539     *        <code>BooleanFalseValue</code>.
540     * @throws SMSException if an error is encountered when trying to  set.
541     * @throws SSOException if the single sign on token is invalid or expired
542     */
543    public void setBooleanValues(
544        String trueValue,
545        String trueValueI18nKey,
546        String falseValue,
547        String falseValueI18nKey
548    ) throws SSOException, SMSException {
549      updateBooleanValues(trueValue, trueValueI18nKey,
550            falseValue, falseValueI18nKey, null);
551    }
552
553
554    /**
555     * Returns the string value for <code>BooleanTrueValue</code>.
556     * 
557     * @return the string value for <code>BooleanTrueValue</code>.
558     */
559    public String getTrueValue() {
560        return (as.getTrueValue());
561    }
562
563    /**
564     * Returns the <code>I18N</code> key for <code>BooleanTrueValue</code>.
565     * 
566     * @return the <code>I18N</code> key for <code>BooleanTrueValue</code>.
567     */
568    public String getTrueValueI18NKey() {
569        return (as.getTrueValueI18NKey());
570    }
571
572    /**
573     * Returns the string value for <code>BooleanFalseValue</code>.
574     * 
575     * @return the string value for <code>BooleanFalseValue</code>.
576     */
577    public String getFalseValue() {
578        return (as.getFalseValue());
579    }
580
581    /**
582     * Returns the <code>I18N</code> Key for <code>BooleanFalseValue</code>.
583     * 
584     * @return the <code>I18N</code> Key for <code>BooleanFalseValue</code>.
585     */
586    public String getFalseValueI18NKey() {
587        return (as.getFalseValueI18NKey());
588    }
589
590    /**
591     * Returns true if the attribute is an optional attribute.
592     * 
593     * @return true if the attribute is an optional attribute.
594     */
595    public boolean isOptional() {
596        return (as.isOptional());
597    }
598
599    /**
600     * Returns true if the attribute is a service identifier (i.e., in the case
601     * of LDAP it would be the COS Specifier attribute).
602     * 
603     * @return true if the attribute is service identifier attribute.
604     */
605    public boolean isServiceIdentifier() {
606        return (as.isServiceIdentifier());
607    }
608
609    /**
610     * Checks if the attribute allows to have resource name.
611     * 
612     * @return true if the attribute allows to have resource name; false
613     *         otherwise
614     */
615    public boolean isResourceNameAllowed() {
616        return (as.isResourceNameAllowed());
617    }
618
619    /**
620     * Returns true if the attribute is a service's status attribute.
621     * 
622     * @return true if the attribute is a status attribute.
623     */
624    public boolean isStatusAttribute() {
625        return (as.isStatusAttribute());
626    }
627
628    /**
629     * Method to get service specific attributes. It return the value of the
630     * "any" attribute, if set in the XML schema for the service
631     * 
632     * @return value of "any" attribute
633     */
634    public String getAny() {
635        return (as.getAny());
636    }
637
638    /**
639     * Sets the any attribute.
640     * 
641     * @param a
642     *            value for any attribute.
643     * @throws SMSException
644     *             if an error is encountered when trying to set.
645     * @throws SSOException
646     *             if the single sign on token is invalid or expired.
647     */
648    public void setAny(String a) throws SMSException, SSOException {
649        updateXMLDocument(SMSUtils.ATTRIBUTE_ANY, a);
650    }
651
652    /**
653     * Returns URL of the view bean for the attribute.
654     * 
655     * @return URL for view bean
656     */
657    public String getPropertiesViewBeanURL() {
658        return (as.getPropertiesViewBeanURL());
659    }
660
661    /**
662     * Sets the URL of the view bean for the attribute.
663     * 
664     * @param prop
665     *            properties view bean URL.
666     * @throws SMSException
667     *             if an error is encountered when trying to set.
668     * @throws SSOException
669     *             if the single sign on token is invalid or expired.
670     */
671    public void setPropertiesViewBeanUR(String prop) throws SMSException,
672            SSOException {
673        updateXMLDocument(SMSUtils.ATTRIBUTE_VIEW_BEAN_URL, prop);
674    }
675
676    /**
677     * Returns <code>true</code> if the attribute is searchable;
678     * <code>false</code> otherwise
679     * 
680     * @return <code>true</code> if the attribute is an optional attribute;
681     *         <code>false</code> otherwise
682     */
683    public boolean isSearchable() {
684        return (as.isSearchable());
685    }
686
687    /**
688     * Sets the attribute isSearchable, if value is set to <code>true
689     * </code>,
690     * or <code>false</code>.
691     * 
692     * @param value
693     *            if set to <code>true</code> the attribute will be
694     *            searchable; else searches cannot be performed on this
695     *            attribute.
696     * @throws SMSException
697     *             if an error is encountered when trying to set
698     * @throws SSOException
699     *             if the single sign on token is invalid or expired
700     */
701    public void setSearchable(String value) throws SMSException, SSOException {
702        if ((!(value.toLowerCase()).equals("yes"))
703                && (!(value.toLowerCase()).equals("no"))) {
704            String[] arg = { value };
705            debug.error("AttributeSchema: Invalid isSearchable value");
706            throw new SMSException(SMSEntry.bundle
707                    .getString("sms-invalid-searchable-value")
708                    + ":" + arg, "sms-invalid-searchable-value");
709        }
710        updateXMLDocument(SMSUtils.ISSEARCHABLE, value);
711    }
712
713    /**
714     * Returns a string representation of this <code> AttributeSchema </code>
715     * object.
716     * 
717     * @return String representation of this object
718     */
719    public String toString() {
720        return (as.toString());
721    }
722
723    /**
724     * Method for modifying default values
725     */
726    protected void updateDefaultValues(Set defaultValues) throws SMSException,
727            SSOException {
728        updateDefaultValues(defaultValues, null);
729    }
730
731    /**
732     * Method for modifying default values given the XML document
733     */
734    protected void updateDefaultValues(Set defaultValues, Document doc)
735            throws SMSException, SSOException {
736        // Check if the values are valid
737        if (ss != null) {
738            Map tempattrs = new HashMap(1);
739            tempattrs.put(getName(), defaultValues);
740            ss.validateAttributes(tempattrs);
741        }
742
743        // Check if the attributes have to be encoded
744        boolean encode = false;
745        if (getSyntax().equals(Syntax.PASSWORD)
746                || getSyntax().equals(Syntax.ENCRYPTED_PASSWORD)) {
747            encode = true;
748        }
749
750        // Construct DefaultValues node
751        StringBuffer sb = new StringBuffer(100);
752        sb.append(XML_PREFIX).append(DEFAULT_VALUES_BEGIN);
753        Iterator items = defaultValues.iterator();
754        while (items.hasNext()) {
755            sb.append(VALUE_BEGIN);
756            if (encode) {
757                String encString = (String) items.next();
758                try {
759                    encString = (String) AccessController
760                            .doPrivileged(new EncodeAction(encString));
761                } catch (Throwable e) {
762                    debug.error("AttributeSchema: Unable to encode", e);
763                }
764                sb.append(encString);
765            } else {
766                sb.append(SMSSchema.escapeSpecialCharacters((String) items
767                        .next()));
768            }
769            sb.append(VALUE_END);
770        }
771        sb.append(DEFAULT_VALUES_END);
772        updateXMLDocument(sb, SMSUtils.ATTRIBUTE_DEFAULT_ELEMENT, doc);
773    }
774
775    protected void updateChoiceValues(Map choiceValues) throws SMSException,
776            SSOException {
777        updateChoiceValues(choiceValues, null);
778    }
779
780     protected void updateChoiceValues(Map choiceValues, Document doc)
781            throws SMSException, SSOException {
782        // Construct ChoiceValues
783        StringBuffer sb = new StringBuffer(100);
784        sb.append(XML_PREFIX).append(CHOICE_VALUES_BEGIN);
785        Iterator items = choiceValues.keySet().iterator();
786        while (items.hasNext()) {
787            String[] vals = new String[2];
788            String value = SMSSchema.escapeSpecialCharacters((String) items
789                    .next());
790            String i18nKey = (String) choiceValues.get(value);
791            if (i18nKey == null) {
792                vals[0] = value;
793                sb.append(MessageFormat.format(CHOICE_VALUE, (Object[])vals));
794            } else {
795                vals[0] = i18nKey;
796                vals[1] = value;
797                sb.append(MessageFormat.format(
798                    CHOICE_VALUE_KEY, (Object[])vals));
799            }
800        }
801        sb.append(CHOICE_VALUES_END);
802        updateXMLDocument(sb, SMSUtils.ATTRIBUTE_CHOICE_VALUES_ELEMENT, doc);
803    }
804
805    protected void updateBooleanValues(
806        String trueValue,
807        String trueValueI18nKey,
808        String falseValue,
809        String falseValueI18nKey,
810        Document doc
811    ) throws SMSException, SSOException {
812      // Construct BooleanValues
813      StringBuffer sb = new StringBuffer(100);
814      sb.append(XML_PREFIX).append(BOOLEAN_VALUES_BEGIN);
815
816      String[] trueVals = new String[2];
817        if ((trueValueI18nKey != null) && (trueValue != null)) {
818            trueVals[0] = trueValueI18nKey;
819            trueVals[1] = SMSSchema.escapeSpecialCharacters(trueValue);
820        } else {
821            trueVals[0] = getTrueValueI18NKey();
822            trueVals[1] = getTrueValue();
823        }
824        sb.append(MessageFormat.format(TRUE_BOOLEAN_KEY, (Object[])trueVals));
825
826      String[] falseVals = new String[2];
827      if ((falseValueI18nKey != null) && (falseValue != null)) {
828          falseVals[0] = falseValueI18nKey;
829          falseVals[1] = SMSSchema.escapeSpecialCharacters(falseValue);
830      } else {
831            falseVals[0] = getFalseValueI18NKey();
832            falseVals[1] = getFalseValue();
833      }
834      sb.append(MessageFormat.format(FALSE_BOOLEAN_KEY, (Object[])falseVals));
835
836      sb.append(BOOLEAN_VALUES_END);
837      updateXMLDocument(sb, SMSUtils.ATTRIBUTE_BOOLEAN_VALUES_ELEMENT, doc);
838    }
839
840    protected void updateXMLDocument(StringBuffer sb, String elementName,
841            Document updateDoc) throws SMSException, SSOException {
842        // Update the default element in XML
843        try {
844            // Construct the XML document
845            Document doc = SMSSchema.getXMLDocument(sb.toString(), false);
846            Node node = XMLUtils.getRootNode(doc, elementName);
847
848            // Convert to Schema's document
849            Document schemaDoc = null;
850            if (updateDoc != null) {
851                schemaDoc = updateDoc;
852            } else if (ssm != null) {
853                schemaDoc = ssm.getDocumentCopy();
854            } else {
855                schemaDoc = ps.getDocumentCopy();
856            }
857            Node nNode = schemaDoc.importNode(node, true);
858
859            // Traverse the document to get this attribute element
860            Node schemaNode = null;
861            if (ss != null) {
862                schemaNode = ss.getSchemaNode(schemaDoc);
863            } else {
864                schemaNode = ps.getPluginSchemaNode(schemaDoc);
865            }
866            Node attrSchemaNode = XMLUtils.getNamedChildNode(schemaNode,
867                    SMSUtils.SCHEMA_ATTRIBUTE, SMSUtils.NAME, getName());
868
869            // Try getting OrganizationAttributeSchema if AttributeSchema
870            // node is not there within Organization node.
871            // This will be a special case for idrepo service.
872            if (attrSchemaNode == null) {
873                schemaNode = ss.getOrgAttrSchemaNode(schemaDoc);
874                attrSchemaNode = XMLUtils.getNamedChildNode(schemaNode,
875                    SMSUtils.SCHEMA_ATTRIBUTE, SMSUtils.NAME, getName());
876            }
877            Node oNode = XMLUtils.getChildNode(attrSchemaNode, elementName);
878            if (oNode != null) {
879                attrSchemaNode.replaceChild(nNode, oNode);
880            } else {
881                attrSchemaNode.appendChild(nNode);
882            }
883            // Update the schema in the directory
884            if (updateDoc != null) {
885                // do nothing
886            } else if (ssm != null) {
887                ssm.replaceSchema(schemaDoc);
888            } else {
889                ps.replacePluginSchema(schemaDoc);
890            }
891        } catch (Exception e) {
892            throw (new SMSException(e.getMessage(), e,
893                    "sms-cannot-update-xml-document"));
894        }
895    }
896
897    /**
898     * update attribute value in attribute schema element
899     */
900    protected void updateXMLDocument(String attrName, String attrValue)
901            throws SMSException, SSOException {
902        // Update the default element in XML
903        try {
904            // Construct the XML document
905            Document schemaDoc = null;
906            if (ssm != null) {
907                schemaDoc = ssm.getDocumentCopy();
908            } else {
909                schemaDoc = ps.getDocumentCopy();
910            }
911
912            // Traverse the document to get this attribute element
913            Node schemaNode = null;
914            if (ss != null) {
915                schemaNode = ss.getSchemaNode(schemaDoc);
916            } else {
917                schemaNode = ps.getPluginSchemaNode(schemaDoc);
918            }
919
920            Node attrSchemaNode = XMLUtils.getNamedChildNode(schemaNode,
921                    SMSUtils.SCHEMA_ATTRIBUTE, SMSUtils.NAME, getName());
922            ((Element) attrSchemaNode).setAttribute(attrName, attrValue);
923
924            // Update the schema in the directory
925            if (ssm != null) {
926                ssm.replaceSchema(schemaDoc);
927            } else {
928                ps.replacePluginSchema(schemaDoc);
929            }
930        } catch (Exception e) {
931            throw (new SMSException(e.getMessage(), e,
932                    "sms-cannot-update-xml-document"));
933        }
934    }
935
936    /**
937     * The class <code>Type</code> defines the types of schema attributes and
938     * provides static constants for these types. This could also be viewed as a
939     * higher level structured data types like Set, List, etc. The primitive
940     * data types are defined by <code>Syntax</code>. Currently defined
941     * schema attribute types are <code>SINGLE</code>, <code>LIST</code>,
942     * <code>SINGLE_CHOICE</code>, <code>MULTIPLE_CHOICE</code>,
943     * <code>SIGNATURE</code> and <code>VALIDATOR</code>.
944     */
945    public static class Type extends Object {
946
947        /**
948         * The <code>SINGLE</code> attribute type specifies that the attribute
949         * can have only a single value.
950         */
951        public static final Type SINGLE = new Type("single");
952
953        /**
954         * The <code>LIST</code> attribute type specifies that the attribute
955         * can have multiple values, i.e., multi-valued attribute.
956         */
957        public static final Type LIST = new Type("list");
958
959        /**
960         * The <code>SINGLE_CHOICE</code> attribute type specifies that the
961         * attribute can have value defined by the <code>getChoiceValues</code>
962         * method of <code>AttributeSchema</code>.
963         */
964        public static final Type SINGLE_CHOICE = new Type("single_choice");
965
966        /**
967         * The <code>MULTIPLE_CHOICE</code> attribute type specifies that the
968         * attribute can have multiple values defined by the
969         * <code>getChoiceValues</code> method of <code>AttributeSchema</code>.
970         */
971        public static final Type MULTIPLE_CHOICE = new Type("multiple_choice");
972
973        /**
974         * The <code>SIGNATURE</code> attribute type specifies that the
975         * attribute is a signing attribute.
976         */
977        public static final Type SIGNATURE = new Type("signature");
978
979        /**
980         * The <code>VALIDATOR</code> attribute type specifies that the
981         * attribute defines a attribute validator plugin.
982         */
983        public static final Type VALIDATOR = new Type("validator");
984
985        private String attrType;
986
987        private Type() {
988        }
989
990        private Type(String type) {
991            attrType = type;
992        }
993
994        /**
995         * The method returns the string representation of the schema attribute
996         * type.
997         * 
998         * @return String string representation of schema attribute type
999         */
1000        public String toString() {
1001            return attrType;
1002        }
1003
1004        /**
1005         * Method to check if two schema attribute types are equal.
1006         * 
1007         * @param schemaAttrType
1008         *            the reference object with which to compare
1009         * 
1010         * @return <code>true</code> if the objects are same; <code>
1011         * false</code>
1012         *         otherwise
1013         */
1014        public boolean equals(Object schemaAttrType) {
1015            if (schemaAttrType instanceof Type) {
1016                Type s = (Type) schemaAttrType;
1017                return (s.attrType.equals(attrType));
1018            }
1019            return (false);
1020        }
1021
1022        /**
1023         * Returns a hash code value for the object.
1024         * 
1025         * @return a hash code value for the object
1026         */
1027        public int hashCode() {
1028            return attrType.hashCode();
1029        }
1030    }
1031
1032    /**
1033     * The class <code>UIType</code> defines the UI types of schema attributes
1034     * and provides static constants for these types. These types mainly will be
1035     * used by the GUI to determine how to display the schema attributes.
1036     * Currently defined schema attribute UI types are <code>RADIO</code>,
1037     * <code>LINK</code>, <code>BUTTON</code> and
1038     * <code>NAME_VALUE_LIST</code>
1039     */
1040    public static class UIType extends Object {
1041
1042        /**
1043         * The <code>RADIO</code> attribute type specifies that the attribute
1044         * should be display as radio button.
1045         */
1046        public static final UIType RADIO = new UIType("radio");
1047
1048        /**
1049         * The <code>LINK</code> attribute type specifies that the attribute
1050         * should be display as a link.
1051         */
1052        public static final UIType LINK = new UIType("link");
1053
1054        /**
1055         * The <code>BUTTON</code> attribute type specifies that the attribute
1056         * should be display as a button.
1057         */
1058        public static final UIType BUTTON = new UIType("button");
1059
1060        /**
1061         * The <code>NAME_VALUE_LIST</code> attribute type specifies that the
1062         * attribute should be display as a name value list widget.
1063         */
1064        public static final UIType NAME_VALUE_LIST = new UIType(
1065            "name_value_list");
1066
1067        /**
1068         * The <code>UNORDERED_LIST</code> attribute type specifies that the
1069         * attribute should be display as an unordered list widget.
1070         */
1071        public static final UIType UNORDEREDLIST = new UIType("unorderedlist");
1072
1073        /**
1074         * The <code>ORDERED_LIST</code> attribute type specifies that the
1075         * attribute should be display as an ordered list widget.
1076         */
1077        public static final UIType ORDEREDLIST = new UIType("orderedlist");
1078
1079        /**
1080         * The <code>MAP_LIST</code> attribute type specifies that the
1081         * attribute should be display as an map list widget.
1082         */
1083        public static final UIType MAPLIST = new UIType("maplist");
1084
1085        /**
1086         * The <code>GLOBALMAP_LIST</code> attribute type specifies that the
1087         * attribute should be display as a global map list widget.
1088         */
1089        public static final UIType GLOBALMAPLIST = new UIType("globalmaplist");
1090        
1091        /**
1092         * The <code>ADDREMOVELIST</code> attribute type specifies that the
1093         * multiple choice attribute should be display as add remove list
1094         * widget.
1095         */
1096        public static final UIType ADDREMOVELIST = new UIType("addremovelist");
1097
1098        private String attrType;
1099
1100        private UIType() {
1101        }
1102
1103        private UIType(String type) {
1104            attrType = type;
1105        }
1106
1107        /**
1108         * The method returns the string representation of the schema attribute
1109         * UI type.
1110         * 
1111         * @return String string representation of schema attribute UI type
1112         */
1113        public String toString() {
1114            return attrType;
1115        }
1116
1117        /**
1118         * Method to check if two schema attribute UI types are equal.
1119         * 
1120         * @param schemaAttrType
1121         *            the reference object with which to compare
1122         * 
1123         * @return <code>true</code> if the objects are same; <code>
1124         * false</code>
1125         *         otherwise
1126         */
1127        public boolean equals(Object schemaAttrType) {
1128            if (schemaAttrType instanceof UIType) {
1129                UIType s = (UIType) schemaAttrType;
1130                return (s.attrType.equals(attrType));
1131            }
1132            return (false);
1133        }
1134
1135        /**
1136         * Returns a hash code value for the object.
1137         * 
1138         * @return a hash code value for the object
1139         */
1140        public int hashCode() {
1141            return attrType.hashCode();
1142        }
1143    }
1144
1145    /**
1146     * The class <code>Syntax</code> defines the syntax of the schema
1147     * attributes and provides static constants for these types. In other words,
1148     * this class defines the primitive data types for the schema attributes.
1149     */
1150    public static class Syntax {
1151
1152        /**
1153         * The <code>BOOLEAN</code> attribute syntax specifies that the
1154         * attribute is of boolean type, i.e., can have a value of either
1155         * <code>true</code> or <code>
1156         * false</code>
1157         */
1158        public static final Syntax BOOLEAN = new Syntax("boolean");
1159
1160        /**
1161         * The <code>EMAIL</code> attribute syntax specifies that the
1162         * attribute is a email address.
1163         */
1164        public static final Syntax EMAIL = new Syntax("email");
1165
1166        /**
1167         * The <code>URL</code> attribute syntax specifies that the attribute
1168         * is a URL.
1169         */
1170        public static final Syntax URL = new Syntax("url");
1171
1172        /**
1173         * The <code>STRING</code> attribute syntax specifies that the
1174         * attribute is of text type, i.e., can have any unicode characters.
1175         */
1176        public static final Syntax STRING = new Syntax("string");
1177
1178        /**
1179         * The <code>PARAGRAPH</code> attribute syntax specifies that the
1180         * attribute is of multi-lined text type.
1181         */
1182        public static final Syntax PARAGRAPH = new Syntax("paragraph");
1183
1184        /**
1185         * The <code>XML</code> attribute syntax specifies that the attribute
1186         * is of XML type, i.e., can have any unicode characters.
1187         */
1188        public static final Syntax XML = new Syntax("xml");
1189
1190        /**
1191         * The <code>PASSWORD</code> attribute syntax specifies that the
1192         * attribute is of password type, will be used by UI to mask the
1193         * password typed.
1194         */
1195        public static final Syntax PASSWORD = new Syntax("password");
1196
1197        /**
1198         * The <code>ENCRYPTED PASSWORD</code> attribute syntax specifies that
1199         * the attribute is of password type, will be used by UI to mask the
1200         * password typed.
1201         */
1202        public static final Syntax ENCRYPTED_PASSWORD = new Syntax(
1203                "encrypted_password");
1204
1205        /**
1206         * The <code>DATE</code> attribute syntax specifies that the attribute
1207         * is of date type.
1208         */
1209        public static final Syntax DATE = new Syntax("date");
1210
1211        /**
1212         * The <code>NUMERIC</code> attribute syntax specifies that the
1213         * attribute is numeric, i.e., can have numbers only.
1214         */
1215        public static final Syntax NUMERIC = new Syntax("numeric");
1216
1217        /**
1218         * The <code>NUMBER</code> attribute syntax specifies that the
1219         * attribute is a number.
1220         */
1221        public static final Syntax NUMBER = new Syntax("number");
1222
1223        /**
1224         * The <code>DECIMAL</code> attribute syntax specifies that the
1225         * attribute is a decimal value.
1226         */
1227        public static final Syntax DECIMAL = new Syntax("decimal");
1228
1229        /**
1230         * The <code>PERCENT</code> attribute syntax specifies that the
1231         * attribute is a percentage.
1232         */
1233        public static final Syntax PERCENT = new Syntax("percent");
1234
1235        /**
1236         * The <code>NUMBER_RANGE</code> attribute syntax specifies that the
1237         * attribute is a number within a range.
1238         */
1239        public static final Syntax NUMBER_RANGE = new Syntax("number_range");
1240
1241        /**
1242         * The <code>DECIMAL_RANGE</code> attribute syntax specifies that the
1243         * attribute is a decimal number within a range.
1244         */
1245        public static final Syntax DECIMAL_RANGE = new Syntax("decimal_range");
1246
1247        /**
1248         * The <code>DECIMAL_NUMBER</code> attribute syntax specifies that the
1249         * attribute is a floating point number, e.g., 1.5, 3.56, etc.
1250         */
1251        public static final Syntax DECIMAL_NUMBER = 
1252            new Syntax("decimal_number");
1253
1254        /**
1255         * The <code>DN</code> attribute syntax specifies that the attribute
1256         * should be an LDAP distinguished name (DN).
1257         */
1258        public static final Syntax DN = new Syntax("dn");
1259
1260        private String attrSyntax;
1261
1262        private Syntax() {
1263        }
1264
1265        private Syntax(String syntax) {
1266            attrSyntax = syntax;
1267        }
1268
1269        /**
1270         * The method returns the string representation of the schema attribute
1271         * syntax.
1272         * 
1273         * @return String string representation of schema attribute syntax
1274         */
1275        public String toString() {
1276            return (attrSyntax);
1277        }
1278
1279        /**
1280         * Method to check if two schema attribute syntax are equal.
1281         * 
1282         * @param schemaAttrSyntax
1283         *            the reference object with which to compare
1284         * 
1285         * @return <code>true</code> if the objects are same; <code>
1286         * false</code>
1287         *         otherwise
1288         */
1289        public boolean equals(Object schemaAttrSyntax) {
1290            if (schemaAttrSyntax instanceof Syntax) {
1291                Syntax s = (Syntax) schemaAttrSyntax;
1292                return (s.attrSyntax.equals(attrSyntax));
1293            }
1294            return (false);
1295        }
1296
1297        /**
1298         * Returns a hash code value for the object.
1299         * 
1300         * @return a hash code value for the object
1301         */
1302        public int hashCode() {
1303            return attrSyntax.hashCode();
1304        }
1305    }
1306
1307    private static final String XML_PREFIX = 
1308        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1309
1310    private static final String DEFAULT_VALUES_BEGIN = "<"
1311            + SMSUtils.ATTRIBUTE_DEFAULT_ELEMENT + ">";
1312
1313    private static final String DEFAULT_VALUES_END = "</"
1314            + SMSUtils.ATTRIBUTE_DEFAULT_ELEMENT + ">";
1315
1316    static final String VALUE_BEGIN = "<" + SMSUtils.ATTRIBUTE_VALUE + ">";
1317
1318    static final String VALUE_END = "</" + SMSUtils.ATTRIBUTE_VALUE + ">";
1319
1320    private static final String CHOICE_VALUES_BEGIN = "<"
1321            + SMSUtils.ATTRIBUTE_CHOICE_VALUES_ELEMENT + ">";
1322
1323    private static final String CHOICE_VALUES_END = "</"
1324            + SMSUtils.ATTRIBUTE_CHOICE_VALUES_ELEMENT + ">";
1325
1326    private static final String CHOICE_VALUE_KEY = "<"
1327            + SMSUtils.ATTRIBUTE_CHOICE_VALUE_ELEMENT + " " + SMSUtils.I18N_KEY
1328            + "=\"{0}\">{1}</" + SMSUtils.ATTRIBUTE_CHOICE_VALUE_ELEMENT + ">";
1329
1330    private static final String CHOICE_VALUE = "<"
1331            + SMSUtils.ATTRIBUTE_CHOICE_VALUE_ELEMENT + ">{0}</"
1332            + SMSUtils.ATTRIBUTE_CHOICE_VALUE_ELEMENT + ">";
1333
1334    private static final String BOOLEAN_VALUES_BEGIN =
1335      "<" + SMSUtils.ATTRIBUTE_BOOLEAN_VALUES_ELEMENT + ">";
1336
1337    private static final String BOOLEAN_VALUES_END =
1338      "</" + SMSUtils.ATTRIBUTE_BOOLEAN_VALUES_ELEMENT + ">";
1339
1340    private static final String TRUE_BOOLEAN_KEY =
1341      "<" + SMSUtils.ATTRIBUTE_TRUE_BOOLEAN_ELEMENT +
1342      " " + SMSUtils.I18N_KEY + "=\"{0}\">{1}</" +
1343      SMSUtils.ATTRIBUTE_TRUE_BOOLEAN_ELEMENT + ">";
1344
1345    private static final String FALSE_BOOLEAN_KEY =
1346      "<" + SMSUtils.ATTRIBUTE_FALSE_BOOLEAN_ELEMENT +
1347      " " + SMSUtils.I18N_KEY + "=\"{0}\">{1}</" +
1348      SMSUtils.ATTRIBUTE_FALSE_BOOLEAN_ELEMENT + ">";
1349}