001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2006 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: Rule.java,v 1.8 2009/11/13 23:52:20 asyhuang Exp $
026 *
027 */
028/*
029 * Portions Copyrighted [2011] [ForgeRock AS]
030 */
031package com.sun.identity.policy;
032
033import java.util.*;
034
035import org.w3c.dom.*;
036
037import com.sun.identity.shared.xml.XMLUtils;
038import com.iplanet.sso.SSOException;
039
040/**
041 * The class <code>Rule</code> provides interfaces to manage
042 * a rule that can be added to a policy.
043 * A rule constains the rule name, service type,
044 * a resource and a map containing action names and action values.
045 *
046 * @supported.api
047 */
048public class Rule extends Object implements Cloneable {
049
050    public static final String EMPTY_RESOURCE_NAME = "";
051    public static final String EXCLUDED_RESOURCE_NAMES = "EXCLUDED_resource_NAMES";
052
053    // Name of the rule
054    private String ruleName;
055
056    // Service type
057    private String serviceTypeName;
058    private ServiceType serviceType;
059
060    // Resource for which the rule applies
061    Set<String> resourceNames = new HashSet<String>();
062    Set<String> excludedResourceNames;
063    private String applicationName;
064
065    // Actions allowed on the resource
066    private Map actions;
067
068    /**
069     * Contruct a <code>Rule</code>
070     */
071    protected Rule() {
072        // do nothing
073    }
074
075    /**
076     * Constructor to create a rule object with the
077     * service name, resource name and actions. The actions
078     * provided as a <code>Map</code> must have the action
079     * name as key and a <code>Set</code> of <code>String</code>s
080     * as its value. The action names and action values must
081     * conform to the schema specified for the service.
082     * Otherwise, <code>InvalidNameException
083     * </code> is thrown. The parameters <code>ruleName</code>
084     * and <code>resourceName</code> can be <code>null</code>.
085     *
086     * @param serviceName name of the service type as defined by
087     * the service schema
088     * @param resourceName name of the resource for the service type
089     * @param actions map of action and action values for the resource
090     *
091     * @exception NameNotFoundException the service name provided does
092     * not exist
093     * @exception InvalidNameException the resource name, action name, 
094     * or values is not valid
095     * @supported.api
096     */
097    public Rule(String serviceName, String resourceName, Map actions) throws
098            NameNotFoundException, InvalidNameException {
099        this(null, serviceName, resourceName, actions);
100    }
101
102    /**
103     * Constructor to create a rule object with the
104     * service name and actions. This is useful for
105     * services (and possibly action names) that do not have
106     * resource names. The actions
107     * provided as a <code>Map</code> must have the action
108     * name as it key and a <code>Set</code> of <code>String</code>s
109     * as its value. The action names and action values must
110     * conform to the schema specified for the service.
111     * Otherwise, <code>InvalidNameException
112     * </code> is thrown. The parameters <code>ruleName</code>
113     * and <code>resourceName</code> can be <code>null</code>.
114     *
115     * @param serviceName name of the service type as defined by
116     * the service schema
117     * @param actions map of action and action values for the resource
118     *
119     * @exception NameNotFoundException the service name provided does
120     * not exist
121     * @exception InvalidNameException the resource name, action name, 
122     * or values is not valid
123     * @supported.api
124     */
125    public Rule(String serviceName, Map actions) throws
126            NameNotFoundException, InvalidNameException {
127        this(null, serviceName, null, actions);
128    }
129
130    /**
131     * Constructor to create a rule object with rule name,
132     * service name, resource name and actions. The actions
133     * provided as a <code>Map</code> must have the action
134     * name as it key and a <code>Set</code> of <code>String</code>s
135     * as its value. The action names and action values must
136     * conform to the service schema.
137     * Otherwise, <code>InvalidNameException
138     * </code> is thrown. The parameters <code>ruleName</code>
139     * and <code>resourceName</code> can be <code>null</code>.
140     *
141     * @param ruleName name of the rule
142     * @param serviceName name of the service type as defined by
143     *        the service schema
144     * @param resourceName name of the resource for the service type
145     * @param actions map of action and action values for the resource
146     *
147     * @exception NameNotFoundException the service name provided does
148     * not exist
149     * @exception InvalidNameException the resource name, action name, 
150     * or values is not valid
151     * @supported.api
152     */
153    public Rule(String ruleName, String serviceName,
154            String resourceName, Map actions) throws
155            NameNotFoundException, InvalidNameException {
156        // Rule and resource name can be null
157        this.ruleName = (ruleName != null) ? ruleName :
158            ("rule" + ServiceTypeManager.generateRandomName());
159        this.resourceNames = new HashSet<String>();
160        this.serviceTypeName = serviceName;
161
162        if ((resourceName == null) || (resourceName.length() == 0)) {
163            resourceNames.add(EMPTY_RESOURCE_NAME);
164        } else {
165            resourceName = resourceName.trim();
166
167            if (PolicyManager.isMigratedToEntitlementService()) {
168                resourceNames.add(resourceName);
169            } else {
170                // Check the service type name
171                checkAndSetServiceType(serviceName);
172                
173                // Verify the action names
174                serviceType.validateActionValues(actions);
175                this.actions = new HashMap(actions);
176
177                try {
178                    resourceNames.add(serviceType.canonicalize(resourceName));
179                } catch (PolicyException pe) {
180                    throw new InvalidNameException(pe, resourceName, 2);
181                }
182            }
183        }
184        
185        // Verify the action names
186        //serviceType.validateActionValues(actions);
187        this.actions = new HashMap(actions);
188    }
189
190    /**
191     * Sets application Name.
192     *
193     * @param applicationName Application name.
194     */
195    public void setApplicationName(String applicationName) {
196        this.applicationName = applicationName;
197    }
198
199    /**
200     * Returns application name.
201     * 
202     * @return application name.
203     */
204    public String getApplicationName() {
205        return (applicationName == null) ? serviceTypeName : applicationName;
206    }
207
208
209    /**
210     * Constructor to create a <code>Rule</code> object from a XML Node
211     * @param ruleNode XML node representation of <code>Rule</code>
212     */
213    protected Rule(Node ruleNode) throws InvalidFormatException,
214            InvalidNameException, NameNotFoundException {
215        // Make sure the node name is rule
216        if (!ruleNode.getNodeName().equalsIgnoreCase(
217                PolicyManager.POLICY_RULE_NODE)) {
218            if (PolicyManager.debug.warningEnabled()) {
219                PolicyManager.debug.warning(
220                        "invalid rule xml blob given to constructor");
221            }
222            throw (new InvalidFormatException(ResBundleUtils.rbName,
223                    "invalid_xml_rule_node", null, "", PolicyException.RULE));
224        }
225
226        // Get rule name, can be null
227        if ((ruleName = XMLUtils.getNodeAttributeValue(ruleNode,
228                PolicyManager.NAME_ATTRIBUTE)) == null) {
229            ruleName = "rule" + ServiceTypeManager.generateRandomName();
230        }
231
232        // Get the service type name, cannot be null
233        Node serviceNode = XMLUtils.getChildNode(ruleNode,
234                PolicyManager.POLICY_RULE_SERVICE_NODE);
235        if ((serviceNode == null) || ((serviceTypeName =
236                XMLUtils.getNodeAttributeValue(serviceNode,
237                PolicyManager.NAME_ATTRIBUTE)) == null)) {
238            if (PolicyManager.debug.warningEnabled()) {
239                PolicyManager.debug.warning(
240                        "invalid service name in rule xml blob in constructor");
241            }
242            String objs[] = {((serviceTypeName == null) ? "null" : serviceTypeName)};
243            throw (new InvalidFormatException(ResBundleUtils.rbName,
244                    "invalid_xml_rule_service_name", objs,
245                    ruleName, PolicyException.RULE));
246        }
247        checkAndSetServiceType(serviceTypeName);
248
249        Node applicationNameNode = XMLUtils.getChildNode(ruleNode,
250            PolicyManager.POLICY_RULE_APPLICATION_NAME_NODE);
251        if (applicationNameNode != null) {
252            applicationName = XMLUtils.getNodeAttributeValue(
253                applicationNameNode, PolicyManager.NAME_ATTRIBUTE);
254        }
255
256        resourceNames = new HashSet<String>();
257        resourceNames.addAll(getResources(ruleNode,
258            PolicyManager.POLICY_RULE_RESOURCE_NODE,
259                PolicyManager.isMigratedToEntitlementService()));
260        
261        Set<String> excludeResources = getResources(ruleNode,
262            PolicyManager.POLICY_RULE_EXCLUDED_RESOURCE_NODE,
263                PolicyManager.isMigratedToEntitlementService());
264        if (excludeResources != null) {
265            excludedResourceNames = new HashSet<String>();
266            excludedResourceNames.addAll(excludeResources);
267        }
268
269        // Get the actions and action values, cannot be null
270        Set actionNodes = XMLUtils.getChildNodes(ruleNode,
271                PolicyManager.ATTR_VALUE_PAIR_NODE);
272        actions = new HashMap();
273        if (actionNodes != null) {
274            Iterator items = actionNodes.iterator();
275            while (items.hasNext()) {
276                // Get action name & values
277                String actionName = null;
278                Set actionValues = null;
279                Node node = (Node) items.next();
280                Node attrNode = XMLUtils.getChildNode(node, PolicyManager.ATTR_NODE);
281                if ((attrNode == null) || ((actionName = XMLUtils.getNodeAttributeValue(attrNode,
282                        PolicyManager.NAME_ATTRIBUTE)) == null) || ((actionValues =
283                        XMLUtils.getAttributeValuePair(node)) == null)) {
284                    String objs[] = {((actionName == null) ? "null" : actionName)};
285                    throw (new InvalidFormatException(
286                            ResBundleUtils.rbName,
287                            "invalid_xml_rule_action_name", objs,
288                            ruleName, PolicyException.RULE));
289                }
290                actions.put(actionName, actionValues);
291
292            }
293            // Validate the action values
294            //serviceType.validateActionValues(actions);
295        }
296    }
297
298    private Set<String> getResources(
299        Node ruleNode,
300        String childNodeName,
301        boolean isMigratedToEntitlementService
302    ) throws InvalidNameException {
303        Set<String> container = null;
304        Set children = XMLUtils.getChildNodes(ruleNode, childNodeName);
305
306        if ((children != null) && !children.isEmpty()) {
307            container = new HashSet<String>();
308            for (Iterator i = children.iterator(); i.hasNext();) {
309                Node resourceNode = (Node) i.next();
310                String resourceName = XMLUtils.getNodeAttributeValue(
311                    resourceNode, PolicyManager.NAME_ATTRIBUTE);
312                if (resourceName != null) {
313                    resourceName = resourceName.trim();
314                    if (!PolicyManager.isMigratedToEntitlementService()) {
315                        try {
316                            resourceName = serviceType.canonicalize(
317                                resourceName);
318                        } catch (PolicyException pe) {
319                            throw new InvalidNameException(pe, resourceName, 2);
320                        }
321                    }
322                    container.add(resourceName);
323                }
324            }
325        }
326        return container;
327    }
328
329    /**
330     * Sets the service type name of this object
331     * @param serviceTypeName service type name for this object
332     * @exception NameNotFoundException the service type name provided does
333     * not exist
334     */
335    private void checkAndSetServiceType(String serviceTypeName)
336            throws NameNotFoundException {
337        // Check the service type name
338        ServiceTypeManager stm = null;
339        try {
340            stm = ServiceTypeManager.getServiceTypeManager();
341            serviceType = stm.getServiceType(serviceTypeName);
342        } catch (SSOException ssoe) {
343            PolicyManager.debug.error("Unable to get admin SSO token" + ssoe);
344            throw (new NameNotFoundException(ssoe,
345                    serviceTypeName, PolicyException.SERVICE));
346        } catch (NameNotFoundException e) {
347            if (!PolicyManager.isMigratedToEntitlementService()) {
348                throw e;
349            }
350        }
351    }
352
353    /**
354     * Returns the name assigned to the rule. It could be <code>null</code>
355     * if it was not constructed with a name.
356     *
357     * @return rule name
358     * @supported.api
359     */
360    public String getName() {
361        return (ruleName);
362    }
363
364    /**
365     * Sets the name for the rule. If a name has already been
366     * assigned, it will be replaced with the given name.
367     *
368     * @param ruleName rule name.
369     * @throws InvalidNameException if rule name is invalid.
370     * @supported.api
371     */
372    public void setName(String ruleName) throws InvalidNameException {
373        if (ruleName != null) {
374            this.ruleName = ruleName;
375        } else {
376            this.ruleName = "rule" + ServiceTypeManager.generateRandomName();
377        }
378    }
379
380    /**
381     * Returns the service name for which the rule has been created.
382     * The service name of the rule cannot be changed once the rule is 
383     * created.
384     *
385     * @return service name
386     * @supported.api
387     */
388    public String getServiceTypeName() {
389        return (serviceTypeName);
390    }
391
392    /**
393     * Returns the resource name for which the rule has been created.
394     * If the service does not support resource names, the method
395     * will return <code>null</code>. The resource name of
396     * the rule cannot be changed once the rule is created.
397     *
398     * @return resource name
399     * @supported.api
400     */
401    public String getResourceName() {
402        return ((resourceNames == null) || resourceNames.isEmpty()) ?
403            EMPTY_RESOURCE_NAME : resourceNames.iterator().next();
404    }
405
406    /**
407     * Returns the resource names for which the rule has been created.
408     * If the service does not support resource names, the method
409     * will return <code>null</code>. The resource name of
410     * the rule cannot be changed once the rule is created.
411     *
412     * @return resource name
413     * @supported.api
414     */
415    public Set<String> getResourceNames() {
416        return resourceNames;
417    }
418
419    /**
420     * Sets the resource names for which the rule has been created.
421     * If the service does not support resource names, the method
422     * will return <code>null</code>. The resource name of
423     * the rule cannot be changed once the rule is created.
424     *
425     * @param resourceNames resource name
426     * @supported.api
427     */
428    public void setResourceNames(Set<String> resourceNames) {
429        this.resourceNames = new HashSet<String>();
430        if (resourceNames != null) {
431            this.resourceNames.addAll(resourceNames);
432        }
433    }
434
435
436    /**
437     * Returns the excluded resource names for which the rule should not apply.
438     * If the service does not support resource names, the method
439     * will return <code>null</code>.
440     * @return excluded resource names
441     * @supported.api
442     */
443    public Set<String> getExcludedResourceNames() {
444        return excludedResourceNames;
445    }
446
447    /**
448     * Sets the excluded resource names for which the rule should not apply.
449     * @param excludedResourceNames excluded resource names
450     * @supported.api
451     */
452    public void setExcludedResourceNames(
453            Set<String> excludedResourceNames) {
454        if (excludedResourceNames != null) {
455            this.excludedResourceNames = new HashSet();
456            this.excludedResourceNames.addAll(excludedResourceNames);
457        } else {
458            this.excludedResourceNames = null;
459        }
460    }
461
462    /**
463     * Returns the action names that have been set for the rule.
464     * The action names returned could be the same as the service's
465     * action names or a subset of it.
466     *
467     * @return action names defined in this rule for the service
468     * @supported.api
469     */
470    public Set getActionNames() {
471        return (new HashSet(actions.keySet()));
472    }
473
474    /**
475     * Returns a set of action values that have been set for the
476     * specified action name.
477     *
478     * @param actionName action name for which to compute values.
479     * @return action names defined in this rule for the service
480     * @throws NameNotFoundException if actions name is not
481     *         found in the rule
482     * @supported.api
483     */
484    public Set getActionValues(String actionName)
485            throws NameNotFoundException {
486        Set<String> answer = (Set<String>) actions.get(actionName);
487        if (answer != null) {
488            Set clone = new HashSet();
489            clone.addAll(answer);
490            return clone;
491        }
492        return (answer);
493    }
494
495    /**
496     * Returns a <code>Map</code> of all action names and their
497     * corresponding action values that have been set in the rule.
498     * The "key" of the <code>Map</code> will be the action name
499     * as a string, and its "value" will be a <code>Set</code>
500     * which contains the action values as strings.
501     *
502     * @return all action names and corresponding action values
503     * @supported.api
504     */
505    public Map getActionValues() {
506        return (new HashMap(actions));
507    }
508
509    /**
510     * Sets the action names and their corresponding actions values
511     * (or permissions) for the resource or the service. 
512     *
513     * @param actionValues action names and their corresponding values
514     * @throws InvalidNameException if action name is invalid.
515     * @supported.api
516     */
517    public void setActionValues(Map actionValues)
518            throws InvalidNameException {
519        serviceType.validateActionValues(actionValues);
520        actions = new HashMap(actionValues);
521    }
522
523    /**
524     * Checks if two rule objects are identical. Two rules are
525     * identical only if the service name, resource name,
526     * action name and values match.
527     *
528     * @param obj object againt which this rule object
529     * will be checked for equality
530     *
531     * @return <code>true</code> if the service type, resource, actions
532     * and action values match, <code>false</code> otherwise.
533     */
534    @Override
535    public boolean equals(Object obj) {
536        boolean matched = true;
537        if (obj == null || !(obj instanceof Rule)) {
538            return false;
539        }
540        Rule other = (Rule) obj;
541        if (excludedResourceNames == null) {
542            if (other.getExcludedResourceNames() != null) {
543                return false;
544            }
545        } else if (!excludedResourceNames.equals(other.getExcludedResourceNames())) {
546            return false;
547        }
548
549        if (applicationName == null) {
550            if (other.applicationName != null) {
551                return false;
552            }
553        } else if (!applicationName.equals(other.applicationName)) {
554            return false;
555        }
556
557        if (resourceNames == null) {
558            if (other.resourceNames != null) {
559                return false;
560            }
561        } else {
562            if (!resourceNames.equals(other.resourceNames)) {
563                return false;
564            }
565        }
566
567        if (!actions.equals(other.actions)) {
568            return false;
569        }
570        return matched;
571    }
572
573    /**
574     * Compares the given service and resource names with the
575     * service and resource name specified in this rule.
576     * The method returns a <code>ResourceMatch</code> object which
577     * specifies if the resources match exactly, do not match, or one
578     * of them is a subordinate resource of the other. If the
579     * service name does not match, the method returns <code>
580     * NO_MATCH</code>.
581     *
582     * @param serviceName name of the service
583     * @param resourceName name of the resource
584     *
585     * @return returns <code>ResourceMatch</code> that
586     * specifies if the service name and resource name are exact match, or
587     * otherwise.
588     */
589    public ResourceMatch isResourceMatch(
590            String serviceName,
591            String resourceName) {
592        //TODO: account for excludedResourceNames
593        ResourceMatch rm = null;
594        if (!serviceName.equalsIgnoreCase(serviceTypeName)) {
595            rm = ResourceMatch.NO_MATCH;
596        } else {
597            //rm = serviceType.compare(this.resourceName, resourceName);
598            String res = getResourceNames().iterator().next();
599            rm = serviceType.compare(resourceName, res);
600        }
601
602        return rm;
603    }
604
605    /**
606     * Returns an XML string representing the rule.
607     *
608     * @return an XML string representing the rule.
609     * @supported.api
610     */
611    public String toXML() {
612        StringBuilder answer = new StringBuilder(100);
613        answer.append("\n").append("<Rule");
614        if (ruleName != null) {
615            answer.append(" name=\"");
616            answer.append(XMLUtils.escapeSpecialCharacters(ruleName));
617            answer.append("\">");
618        } else {
619            answer.append(">");
620        }
621
622        answer.append("\n").append("<ServiceName name=\"");
623        answer.append(XMLUtils.escapeSpecialCharacters(serviceTypeName));
624        answer.append("\" />");
625
626        if (applicationName != null) {
627            answer.append("\n").append("<")
628                .append(PolicyManager.POLICY_RULE_APPLICATION_NAME_NODE)
629                .append(" name=\"")
630                .append(XMLUtils.escapeSpecialCharacters(applicationName))
631                .append("\" />");
632        }
633
634        if (resourceNames != null) {
635            for (String resourceName : resourceNames) {
636                answer.append("\n").append("<ResourceName name=\"");
637                answer.append(
638                    XMLUtils.escapeSpecialCharacters(resourceName));
639                answer.append("\" />");
640            }
641        }
642        if (excludedResourceNames != null) {
643        for (String r : excludedResourceNames) {
644                answer.append("\n").append("<ExcludedResourceName name=\"");
645                answer.append(
646                    XMLUtils.escapeSpecialCharacters(r));
647                answer.append("\" />");
648            }
649        }
650
651        Set actionNames = new HashSet();
652        actionNames.addAll(actions.keySet());
653
654        Iterator actionNamesIter = actionNames.iterator();
655        while (actionNamesIter.hasNext()) {
656            String actionName = (String) actionNamesIter.next();
657            answer.append("\n").append("<AttributeValuePair>");
658            answer.append("\n").append("<Attribute name=\"");
659            answer.append(XMLUtils.escapeSpecialCharacters(actionName));
660            answer.append("\" />");
661            Set values = (Set) actions.get(actionName);
662
663            if (values.size() > 0) {
664                Iterator items = values.iterator();
665                while (items.hasNext()) {
666                    answer.append("\n").append("<Value>");
667                    answer.append(
668                            XMLUtils.escapeSpecialCharacters(
669                            (String) items.next()));
670                    answer.append("</Value>");
671                }
672
673            }
674            answer.append("\n").append("</AttributeValuePair>");
675        }
676
677        answer.append("\n").append("</Rule>");
678        return (answer.toString());
679    }
680
681    /**
682     * Returns service type of this rules
683     * @return service type of this rule
684     */
685    protected ServiceType getServiceType() {
686        return (serviceType);
687    }
688
689    /**
690     * Returns an XML respresentation of the rule with policy name to
691     * use in resource index tree
692     * @param policyName policy name to use while creating xml representation
693     * @return an XML respresentation of the rule with policy name to
694     * use in resource index tree
695     */
696    protected String toResourcesXml(String policyName) {
697        StringBuffer beginning = new StringBuffer(100);
698        // "<PolicyCrossReferences name=\"" + serviceTypeName +
699        // "\" type=\"Resources\">"
700        beginning.append("<").append(PolicyManager.POLICY_INDEX_ROOT_NODE).append(" ").append(PolicyManager.POLICY_INDEX_ROOT_NODE_NAME_ATTR).append("=\"").append(serviceTypeName).append("\" ").append(PolicyManager.POLICY_INDEX_ROOT_NODE_TYPE_ATTR).append("=\"").append(
701                PolicyManager.POLICY_INDEX_ROOT_NODE_TYPE_ATTR_RESOURCES_VALUE).append("\">");
702
703        String normalizedResName = null;
704        if ((resourceNames == null) || resourceNames.isEmpty()) {
705            normalizedResName = ResourceManager.EMPTY_RESOURCE_NAME;
706        } else {
707            normalizedResName = resourceNames.iterator().next();
708        }
709
710        String[] resources = serviceType.split(normalizedResName);
711        int n = resources.length;
712
713        StringBuilder middle = new StringBuilder(100);
714        // "<Reference name=\"" + resources[n-1]) +
715        // "\"><PolicyName name=\"" + policyName +
716        // "\"/></Reference>"
717        middle.append("<").append(PolicyManager.POLICY_INDEX_REFERENCE_NODE).append(" ").append(PolicyManager.POLICY_INDEX_REFERENCE_NODE_NAME_ATTR).append("=\"").append(resources[n - 1]).append("\"><").append(PolicyManager.POLICY_INDEX_POLICYNAME_NODE).append(" ").append(PolicyManager.POLICY_INDEX_POLICYNAME_NODE_NAME_ATTR).append("=\"").append(policyName).append("\"/></").append(PolicyManager.POLICY_INDEX_REFERENCE_NODE).append(">");
718        String tmp = middle.toString();
719        for (int i = n - 2; i >=
720                0; i--) {
721            //tmp = "<Reference name=\"" + resources[i] +"\">" +
722            //    tmp + "</Reference>";
723            tmp = "<" + PolicyManager.POLICY_INDEX_REFERENCE_NODE +
724                    " " + PolicyManager.POLICY_INDEX_REFERENCE_NODE_NAME_ATTR +
725                    "=\"" + resources[i] + "\">" + tmp + "</" +
726                    PolicyManager.POLICY_INDEX_REFERENCE_NODE + ">";
727        }
728
729        return (beginning + tmp + "</" +
730                PolicyManager.POLICY_INDEX_ROOT_NODE + ">");
731    }
732
733    /**
734     * Returns xml string representation of the rule.
735     *
736     * @return xml string representation of the rule
737     */
738    @Override
739    public String toString() {
740        return (toXML());
741    }
742
743    /**
744     * Creates and returns a copy of this object. The returned
745     * <code>Rule</code> object will have the same rule
746     * name, resource, service name, and actions
747     * such that <code>x.clone().equals(x)</code> will be
748     * <code>true</code>. However <code>x.clone()</code>
749     * will not be the same as <code>x</code>, i.e.,
750     * <code>x.clone() != x</code>.
751     *
752     * @return a copy of this object
753     */
754    @Override
755    public Object clone() {
756        Rule answer = null;
757        try {
758            answer = (Rule) super.clone();
759        } catch (CloneNotSupportedException se) {
760            answer = new Rule();
761        }
762
763        answer.ruleName = ruleName;
764        answer.serviceTypeName = serviceTypeName;
765        answer.applicationName = applicationName;
766        answer.serviceType = serviceType;
767        answer.resourceNames = new HashSet();
768        if (resourceNames != null) {
769            answer.resourceNames.addAll(resourceNames);
770        }
771        
772        if (excludedResourceNames != null) {
773            answer.excludedResourceNames = new HashSet();
774            answer.excludedResourceNames.addAll(excludedResourceNames);
775        }
776
777        // Copy the actions
778        answer.actions = new HashMap();
779        Iterator items = actions.keySet().iterator();
780        while (items.hasNext()) {
781            Object o = items.next();
782            Set set = (Set) actions.get(o);
783            HashSet aset = new HashSet();
784            aset.addAll(set);
785            answer.actions.put(o, aset);
786        }
787
788        return (answer);
789    }
790
791    /**
792     * Returns action values given resource type, resource name and a set of
793     * action names  by matching the arguments to those of the rule object
794     *
795     * @param resourceType resource type
796     * @param resourceName resource name
797     * @param actionNames a set of action names for which to compute values.
798     * Each element of the set should be a <code>String</code>
799     * valued action name
800     * @return a map of action values for actions
801     *         Each key of the map is a String valued action name
802     *         Each value of the map is a set of String values
803     * @throws NameNotFoundException if any name in <code>actionNames</code> is
804     *         not found in the rule.
805     */
806    Map getActionValues(String resourceType, String resourceName,
807            Set actionNames) throws NameNotFoundException {
808        Map actionValues = null;
809        if ((serviceTypeName.equalsIgnoreCase(resourceType)) && (actionNames != null)) {
810            ResourceMatch rm = isResourceMatch(resourceType, resourceName);
811            if (ResourceMatch.EXACT_MATCH.equals(rm) || ResourceMatch.WILDCARD_MATCH.equals(rm)) {
812                //if (ResourceMatch.EXACT_MATCH.equals(rm) ) {
813                actionValues = new HashMap();
814                Iterator actionIter = actionNames.iterator();
815                while (actionIter.hasNext()) {
816                    String actionName = (String) actionIter.next();
817                    Set values = getActionValues(actionName);
818                    if (values != null) {
819                        actionValues.put(actionName, values);
820                    }
821
822                }
823            }
824        }
825        return (actionValues);
826    }
827}