View Javadoc

1   /*
2    * $Id: BasicAttributeContext.java 943645 2010-05-12 19:33:19Z apetrelli $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  package org.apache.tiles;
23  
24  import static org.apache.tiles.CompareUtil.*;
25  
26  import java.io.Serializable;
27  import java.util.HashMap;
28  import java.util.Map;
29  import java.util.Set;
30  
31  
32  /**
33   * Basic implementation for <code>AttributeContext</code>.
34   *
35   * @version $Rev: 943645 $ $Date: 2010-05-13 05:33:19 +1000 (Thu, 13 May 2010) $
36   * @since 2.1.0
37   */
38  public class BasicAttributeContext implements AttributeContext, Serializable {
39  
40      /**
41       * The template attribute, to render a template.
42       *
43       * @since 2.1.2
44       */
45      protected Attribute templateAttribute;
46  
47      /**
48       * Associated ViewPreparer URL or classname, if defined.
49       *
50       * @since 2.1.0
51       */
52      protected String preparer = null;
53  
54      /**
55       * Template attributes.
56       * @since 2.1.0
57       */
58      protected Map<String, Attribute> attributes = null;
59  
60      /**
61       * Cascaded template attributes.
62       * @since 2.1.0
63       */
64      protected Map<String, Attribute> cascadedAttributes = null;
65  
66      /**
67       * Constructor.
68       *
69       * @since 2.1.0
70       */
71      public BasicAttributeContext() {
72      }
73  
74      /**
75       * Constructor.
76       * Create a context and set specified attributes.
77       *
78       * @param attributes Attributes to initialize context.
79       * @since 2.1.0
80       */
81      public BasicAttributeContext(Map<String, Attribute> attributes) {
82          if (attributes != null) {
83              this.attributes = deepCopyAttributeMap(attributes);
84          }
85      }
86  
87      /**
88       * Copy constructor.
89       *
90       * @param context The constructor to copy.
91       * @since 2.1.0
92       */
93      public BasicAttributeContext(AttributeContext context) {
94          if (context instanceof BasicAttributeContext) {
95              copyBasicAttributeContext((BasicAttributeContext) context);
96          } else {
97              Attribute parentTemplateAttribute = context.getTemplateAttribute();
98              if (parentTemplateAttribute != null) {
99                  this.templateAttribute = new Attribute(parentTemplateAttribute);
100             }
101             this.preparer = context.getPreparer();
102             this.attributes = new HashMap<String, Attribute>();
103             Set<String> parentAttributeNames = context.getLocalAttributeNames();
104             if (parentAttributeNames != null) {
105                 for (String name : parentAttributeNames) {
106                     attributes.put(name, new Attribute(context.getLocalAttribute(name)));
107                 }
108             }
109             inheritCascadedAttributes(context);
110         }
111     }
112 
113     /**
114      * Copy constructor.
115      *
116      * @param context The constructor to copy.
117      * @since 2.1.0
118      */
119     public BasicAttributeContext(BasicAttributeContext context) {
120         copyBasicAttributeContext(context);
121     }
122 
123     /** {@inheritDoc} */
124     public Attribute getTemplateAttribute() {
125         return templateAttribute;
126     }
127 
128     /** {@inheritDoc} */
129     public void setTemplateAttribute(Attribute templateAttribute) {
130         this.templateAttribute = templateAttribute;
131     }
132 
133     /** {@inheritDoc} */
134     public String getPreparer() {
135         return preparer;
136     }
137 
138     /** {@inheritDoc} */
139     public void setPreparer(String url) {
140         this.preparer = url;
141     }
142 
143     /** {@inheritDoc} */
144     public void inheritCascadedAttributes(AttributeContext context) {
145         if (context instanceof BasicAttributeContext) {
146             copyCascadedAttributes((BasicAttributeContext) context);
147         } else {
148             this.cascadedAttributes = new HashMap<String, Attribute>();
149             Set<String> parentAttributeNames = context.getCascadedAttributeNames();
150             if (parentAttributeNames != null) {
151                 for (String name : parentAttributeNames) {
152                     cascadedAttributes.put(name, new Attribute(context
153                             .getCascadedAttribute(name)));
154                 }
155             }
156         }
157     }
158 
159     /** {@inheritDoc} */
160     public void inherit(AttributeContext parent) {
161         if (parent instanceof BasicAttributeContext) {
162             inherit((BasicAttributeContext) parent);
163         } else {
164             // Inheriting template, roles and preparer.
165             Attribute parentTemplateAttribute = parent.getTemplateAttribute();
166             inheritParentTemplateAttribute(parentTemplateAttribute);
167             if (preparer == null) {
168                 preparer = parent.getPreparer();
169             }
170 
171             // Inheriting attributes.
172             Set<String> names = parent.getCascadedAttributeNames();
173             if (names != null && !names.isEmpty()) {
174                 for (String name : names) {
175                     Attribute attribute = parent.getCascadedAttribute(name);
176                     Attribute destAttribute = getCascadedAttribute(name);
177                     if (destAttribute == null) {
178                         putAttribute(name, attribute, true);
179                     } else if (attribute instanceof ListAttribute
180                             && destAttribute instanceof ListAttribute
181                             && ((ListAttribute) destAttribute).isInherit()) {
182                         ((ListAttribute) destAttribute).inherit((ListAttribute) attribute);
183                     }
184                 }
185             }
186             names = parent.getLocalAttributeNames();
187             if (names != null && !names.isEmpty()) {
188                 for (String name : names) {
189                     Attribute attribute = parent.getLocalAttribute(name);
190                     Attribute destAttribute = getLocalAttribute(name);
191                     if (destAttribute == null) {
192                         putAttribute(name, attribute, false);
193                     } else if (attribute instanceof ListAttribute
194                             && destAttribute instanceof ListAttribute
195                             && ((ListAttribute) destAttribute).isInherit()) {
196                         ((ListAttribute) destAttribute).inherit((ListAttribute) attribute);
197                     }
198                 }
199             }
200         }
201     }
202 
203     /**
204      * Inherits the attribute context, inheriting, i.e. copying if not present,
205      * the attributes.
206      *
207      * @param parent The attribute context to inherit.
208      * @since 2.1.0
209      */
210     public void inherit(BasicAttributeContext parent) {
211         // Set template, roles and preparer if not set.
212         inheritParentTemplateAttribute(parent.getTemplateAttribute());
213         if (preparer == null) {
214             preparer = parent.preparer;
215         }
216 
217         // Sets attributes.
218         cascadedAttributes = addMissingAttributes(parent.cascadedAttributes,
219                 cascadedAttributes);
220         attributes = addMissingAttributes(parent.attributes, attributes);
221     }
222 
223     /**
224      * Add all attributes to this context.
225      * Copies all of the mappings from the specified attribute map to this context.
226      * New attribute mappings will replace any mappings that this context had for any of the keys
227      * currently in the specified attribute map.
228      *
229      * @param newAttributes Attributes to add.
230      * @since 2.1.0
231      */
232     public void addAll(Map<String, Attribute> newAttributes) {
233         if (newAttributes == null) {
234             return;
235         }
236 
237         if (attributes == null) {
238             attributes = new HashMap<String, Attribute>(newAttributes);
239             return;
240         }
241 
242         attributes.putAll(newAttributes);
243     }
244 
245     /**
246      * Add all missing attributes to this context.
247      * Copies all of the mappings from the specified attributes map to this context.
248      * New attribute mappings will be added only if they don't already exist in
249      * this context.
250      *
251      * @param defaultAttributes Attributes to add.
252      * @since 2.1.0
253      */
254     public void addMissing(Map<String, Attribute> defaultAttributes) {
255         if (defaultAttributes == null) {
256             return;
257         }
258 
259         if (attributes == null) {
260             attributes = new HashMap<String, Attribute>();
261             if (cascadedAttributes == null || cascadedAttributes.isEmpty()) {
262                 attributes.putAll(defaultAttributes);
263                 return;
264             }
265         }
266 
267         Set<Map.Entry<String, Attribute>> entries = defaultAttributes.entrySet();
268         for (Map.Entry<String, Attribute> entry : entries) {
269             String key = entry.getKey();
270             if (!attributes.containsKey(key)
271                     && (cascadedAttributes == null || !cascadedAttributes
272                             .containsKey(key))) {
273                 attributes.put(entry.getKey(), entry.getValue());
274             }
275         }
276     }
277 
278     /** {@inheritDoc} */
279     public Attribute getAttribute(String name) {
280         Attribute retValue = null;
281         if (attributes != null) {
282             retValue = attributes.get(name);
283         }
284 
285         if (retValue == null && cascadedAttributes != null) {
286             retValue = cascadedAttributes.get(name);
287         }
288 
289         return retValue;
290     }
291 
292     /** {@inheritDoc} */
293     public Attribute getLocalAttribute(String name) {
294         if (attributes == null) {
295             return null;
296         }
297 
298         return attributes.get(name);
299     }
300 
301     /** {@inheritDoc} */
302     public Attribute getCascadedAttribute(String name) {
303         if (cascadedAttributes == null) {
304             return null;
305         }
306 
307         return cascadedAttributes.get(name);
308     }
309 
310     /** {@inheritDoc} */
311     public Set<String> getLocalAttributeNames() {
312         if (attributes != null && !attributes.isEmpty()) {
313             return attributes.keySet();
314         }
315         return null;
316     }
317 
318     /** {@inheritDoc} */
319     public Set<String> getCascadedAttributeNames() {
320         if (cascadedAttributes != null && !cascadedAttributes.isEmpty()) {
321             return cascadedAttributes.keySet();
322         }
323         return null;
324     }
325 
326     /** {@inheritDoc} */
327     public void putAttribute(String name, Attribute value) {
328         if (attributes == null) {
329             attributes = new HashMap<String, Attribute>();
330         }
331 
332         attributes.put(name, value);
333     }
334 
335     /** {@inheritDoc} */
336     public void putAttribute(String name, Attribute value, boolean cascade) {
337         Map<String, Attribute> mapToUse;
338         if (cascade) {
339             if (cascadedAttributes == null) {
340                 cascadedAttributes = new HashMap<String, Attribute>();
341             }
342             mapToUse = cascadedAttributes;
343         } else {
344             if (attributes == null) {
345                 attributes = new HashMap<String, Attribute>();
346             }
347             mapToUse = attributes;
348         }
349         mapToUse.put(name, value);
350     }
351 
352     /** {@inheritDoc} */
353     public void clear() {
354         templateAttribute = null;
355         preparer = null;
356         attributes.clear();
357         cascadedAttributes.clear();
358     }
359 
360     /** {@inheritDoc} */
361     @Override
362     public boolean equals(Object obj) {
363         BasicAttributeContext bac = (BasicAttributeContext) obj;
364         return nullSafeEquals(templateAttribute, bac.templateAttribute)
365                 && nullSafeEquals(preparer, bac.preparer)
366                 && nullSafeEquals(attributes, bac.attributes)
367                 && nullSafeEquals(cascadedAttributes, bac.cascadedAttributes);
368     }
369 
370     /** {@inheritDoc} */
371     @Override
372     public int hashCode() {
373         return nullSafeHashCode(templateAttribute) + nullSafeHashCode(preparer)
374                 + nullSafeHashCode(attributes)
375                 + nullSafeHashCode(cascadedAttributes);
376     }
377 
378     /**
379      * Inherits the parent template attribute.
380      *
381      * @param parentTemplateAttribute The parent template attribute.
382      */
383     private void inheritParentTemplateAttribute(
384             Attribute parentTemplateAttribute) {
385         if (parentTemplateAttribute != null) {
386             if (templateAttribute == null) {
387                 templateAttribute = new Attribute(parentTemplateAttribute);
388             } else {
389                 templateAttribute.inherit(parentTemplateAttribute);
390             }
391         }
392     }
393 
394     /**
395      * Copies a BasicAttributeContext in an easier way.
396      *
397      * @param context The context to copy.
398      */
399     private void copyBasicAttributeContext(BasicAttributeContext context) {
400         Attribute parentTemplateAttribute = context.getTemplateAttribute();
401         if (parentTemplateAttribute != null) {
402             this.templateAttribute = new Attribute(parentTemplateAttribute);
403         }
404         preparer = context.preparer;
405         if (context.attributes != null && !context.attributes.isEmpty()) {
406             attributes = deepCopyAttributeMap(context.attributes);
407         }
408         copyCascadedAttributes(context);
409     }
410 
411     /**
412      * Copies the cascaded attributes to the current context.
413      *
414      * @param context The context to copy from.
415      */
416     private void copyCascadedAttributes(BasicAttributeContext context) {
417         if (context.cascadedAttributes != null
418                 && !context.cascadedAttributes.isEmpty()) {
419             cascadedAttributes = deepCopyAttributeMap(context.cascadedAttributes);
420         }
421     }
422 
423     /**
424      * Adds missing attributes to the destination map.
425      *
426      * @param source The source attribute map.
427      * @param destination The destination attribute map.
428      * @return The destination attribute map if not null, a new one otherwise.
429      */
430     private Map<String, Attribute> addMissingAttributes(Map<String, Attribute> source,
431             Map<String, Attribute> destination) {
432         if (source != null && !source.isEmpty()) {
433             if (destination == null) {
434                 destination = new HashMap<String, Attribute>();
435             }
436             for (Map.Entry<String, Attribute> entry : source.entrySet()) {
437                 String key = entry.getKey();
438                 Attribute destAttribute = destination.get(key);
439                 if (destAttribute == null) {
440                     destination.put(key, entry.getValue());
441                 } else if (destAttribute instanceof ListAttribute
442                         && entry.getValue() instanceof ListAttribute
443                         && ((ListAttribute) destAttribute).isInherit()) {
444                     ((ListAttribute) destAttribute)
445                             .inherit((ListAttribute) entry.getValue());
446                 }
447             }
448         }
449 
450         return destination;
451     }
452 
453     /**
454      * Deep copies the attribute map, by creating clones (using copy
455      * constructors) of the attributes.
456      *
457      * @param attributes The attribute map to copy.
458      * @return The copied map.
459      */
460     private Map<String, Attribute> deepCopyAttributeMap(
461             Map<String, Attribute> attributes) {
462         Map<String, Attribute> retValue = new HashMap<String, Attribute>(attributes.size());
463         for (Map.Entry<String, Attribute> entry : attributes.entrySet()) {
464             Attribute toCopy = entry.getValue();
465             if (toCopy != null) {
466                 retValue.put(entry.getKey(), toCopy.clone());
467             }
468         }
469         return retValue;
470     }
471 }