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