View Javadoc

1   /*
2    * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.opensaml.xml.util;
18  
19  import java.util.Collection;
20  import java.util.Collections;
21  import java.util.Map;
22  import java.util.Set;
23  import java.util.concurrent.ConcurrentHashMap;
24  import java.util.concurrent.CopyOnWriteArraySet;
25  
26  import javax.xml.namespace.QName;
27  
28  import org.opensaml.xml.Configuration;
29  import org.opensaml.xml.XMLObject;
30  
31  /**
32   * A map of attribute names and attribute values that invalidates the DOM of the attribute owning XMLObject when the
33   * attributes change.
34   * 
35   * <strong>Note:</strong> 
36   */
37  public class AttributeMap implements Map<QName, String> {
38  
39      /** XMLObject owning the attributes. */
40      private XMLObject attributeOwner;
41  
42      /** Map of attributes. */
43      private Map<QName, String> attributes;
44      
45      /** Set of attribute QNames which have been locally registered as having an ID type within this 
46       * AttributeMap instance. */
47      private Set<QName> idAttribNames;
48  
49      /**
50       * Constructor.
51       *
52       * @param newOwner the XMLObject that owns these attributes
53       * 
54       * @throws NullPointerException thrown if the given XMLObject is null
55       */
56      public AttributeMap(XMLObject newOwner) throws NullPointerException {
57          if (newOwner == null) {
58              throw new NullPointerException("Attribute owner XMLObject may not be null");
59          }
60  
61          attributeOwner = newOwner;
62          attributes = new ConcurrentHashMap<QName, String>();
63          idAttribNames = new CopyOnWriteArraySet<QName>();
64      }
65  
66      /** {@inheritDoc} */
67      public String put(QName attributeName, String value) {
68          String oldValue = get(attributeName);
69          if (value != oldValue) {
70              releaseDOM();
71              attributes.put(attributeName, value);
72              if (isIDAttribute(attributeName) || Configuration.isIDAttribute(attributeName)) {
73                  attributeOwner.getIDIndex().deregisterIDMapping(oldValue);
74                  attributeOwner.getIDIndex().registerIDMapping(value, attributeOwner);
75              }
76          }
77          
78          return oldValue;
79      }
80  
81      /** {@inheritDoc} */
82      public void clear() {
83          for (QName attributeName : attributes.keySet()) {
84              remove(attributeName);
85          }
86      }
87  
88      /**
89       * Returns the set of keys.
90       * 
91       * @return unmodifiable set of keys
92       */
93      public Set<QName> keySet() {
94          return Collections.unmodifiableSet(attributes.keySet());
95      }
96  
97      /** {@inheritDoc} */
98      public int size() {
99          return attributes.size();
100     }
101 
102     /** {@inheritDoc} */
103     public boolean isEmpty() {
104         return attributes.isEmpty();
105     }
106 
107     /** {@inheritDoc} */
108     public boolean containsKey(Object key) {
109         return attributes.containsKey(key);
110     }
111 
112     /** {@inheritDoc} */
113     public boolean containsValue(Object value) {
114         return attributes.containsValue(value);
115     }
116 
117     /** {@inheritDoc} */
118     public String get(Object key) {
119         return attributes.get(key);
120     }
121 
122     /** {@inheritDoc} */
123     public String remove(Object key) {
124         String removedValue = attributes.remove(key);
125         if (removedValue != null) {
126             releaseDOM();
127             QName attributeName = (QName) key;
128             if (isIDAttribute(attributeName) || Configuration.isIDAttribute(attributeName)) {
129                 attributeOwner.getIDIndex().deregisterIDMapping(removedValue);
130             }
131         }
132 
133         return removedValue;
134     }
135 
136     /** {@inheritDoc} */
137     public void putAll(Map<? extends QName, ? extends String> t) {
138         if (t != null && t.size() > 0) {
139             for (Entry<? extends QName, ? extends String> entry : t.entrySet()) {
140                 put(entry.getKey(), entry.getValue());
141             }
142         }
143     }
144 
145     /**
146      * Returns the values in this map.
147      * 
148      * @return an unmodifiable collection of values
149      */
150     public Collection<String> values() {
151         return Collections.unmodifiableCollection(attributes.values());
152     }
153 
154     /**
155      * Returns the set of entries.
156      * 
157      * @return unmodifiable set of entries
158      */
159     public Set<Entry<QName, String>> entrySet() {
160         return Collections.unmodifiableSet(attributes.entrySet());
161     }
162     
163     /**
164      * Register an attribute as having a type of ID.
165      * 
166      * @param attributeName the QName of the ID attribute to be registered
167      */
168     public void registerID(QName attributeName) {
169         if (! idAttribNames.contains(attributeName)) {
170             idAttribNames.add(attributeName);
171         }
172         
173         // In case attribute already has a value,
174         // register the current value mapping with the XMLObject owner.
175         if (containsKey(attributeName)) {
176             attributeOwner.getIDIndex().registerIDMapping(get(attributeName), attributeOwner);
177         }
178     }
179     
180     /**
181      * Deregister an attribute as having a type of ID.
182      * 
183      * @param attributeName the QName of the ID attribute to be de-registered
184      */
185     public void deregisterID(QName attributeName) {
186         if (idAttribNames.contains(attributeName)) {
187             idAttribNames.remove(attributeName);
188         }
189         
190         // In case attribute already has a value,
191         // deregister the current value mapping with the XMLObject owner.
192         if (containsKey(attributeName)) {
193             attributeOwner.getIDIndex().deregisterIDMapping(get(attributeName));
194         }
195     }
196     
197     /**
198      * Check whether a given attribute is locally registered as having an ID type within
199      * this AttributeMap instance.
200      * 
201      * @param attributeName the QName of the attribute to be checked for ID type.
202      * @return true if attribute is registered as having an ID type.
203      */
204     public boolean isIDAttribute(QName attributeName) {
205         return idAttribNames.contains(attributeName);
206     }
207     
208     /**
209      * Releases the DOM caching associated XMLObject and its ancestors.
210      */
211     private void releaseDOM() {
212         attributeOwner.releaseDOM();
213         attributeOwner.releaseParentDOM(true);
214     }
215 }