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