blob: f9fec6b77675a4cbc6a79a45ffa40bf7721065e9 [file] [log] [blame]
/*
* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.tiles;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Basic implementation for <code>AttributeContext</code>.
*
* @version $Rev$ $Date$
* @since 2.1.0
*/
public class BasicAttributeContext implements AttributeContext, Serializable {
/**
* The template attribute, to render a template.
*
* @since 2.1.2
*/
protected Attribute templateAttribute;
/**
* Associated ViewPreparer URL or classname, if defined.
*
* @since 2.1.0
*/
protected String preparer = null;
/**
* Template attributes.
* @since 2.1.0
*/
protected Map<String, Attribute> attributes = null;
/**
* Cascaded template attributes.
* @since 2.1.0
*/
protected Map<String, Attribute> cascadedAttributes = null;
/**
* Constructor.
*
* @since 2.1.0
*/
public BasicAttributeContext() {
super();
}
/**
* Constructor.
* Create a context and set specified attributes.
*
* @param attributes Attributes to initialize context.
* @since 2.1.0
*/
public BasicAttributeContext(Map<String, Attribute> attributes) {
if (attributes != null) {
this.attributes = deepCopyAttributeMap(attributes);
}
}
/**
* Copy constructor.
*
* @param context The constructor to copy.
* @since 2.1.0
*/
public BasicAttributeContext(AttributeContext context) {
if (context instanceof BasicAttributeContext) {
copyBasicAttributeContext((BasicAttributeContext) context);
} else {
Attribute parentTemplateAttribute = context.getTemplateAttribute();
if (parentTemplateAttribute != null) {
this.templateAttribute = new Attribute(parentTemplateAttribute);
}
this.preparer = context.getPreparer();
this.attributes = new HashMap<String, Attribute>();
for (String name : context.getLocalAttributeNames()) {
attributes.put(name, new Attribute(context.getLocalAttribute(name)));
}
inheritCascadedAttributes(context);
}
}
/**
* Copy constructor.
*
* @param context The constructor to copy.
* @since 2.1.0
*/
public BasicAttributeContext(BasicAttributeContext context) {
copyBasicAttributeContext(context);
}
/** {@inheritDoc} */
public Attribute getTemplateAttribute() {
return templateAttribute;
}
/** {@inheritDoc} */
public void setTemplateAttribute(Attribute templateAttribute) {
this.templateAttribute = templateAttribute;
}
/** {@inheritDoc} */
public String getPreparer() {
return preparer;
}
/** {@inheritDoc} */
public void setPreparer(String url) {
this.preparer = url;
}
/** {@inheritDoc} */
public void inheritCascadedAttributes(AttributeContext context) {
if (context instanceof BasicAttributeContext) {
copyCascadedAttributes((BasicAttributeContext) context);
} else {
this.cascadedAttributes = new HashMap<String, Attribute>();
for (String name : context.getCascadedAttributeNames()) {
cascadedAttributes.put(name, new Attribute(context
.getCascadedAttribute(name)));
}
}
}
/** {@inheritDoc} */
public void inherit(AttributeContext parent) {
if (parent instanceof BasicAttributeContext) {
inherit((BasicAttributeContext) parent);
} else {
// Inheriting template, roles and preparer.
Attribute parentTemplateAttribute = parent.getTemplateAttribute();
inheritParentTemplateAttribute(parentTemplateAttribute);
if (preparer == null) {
preparer = parent.getPreparer();
}
// Inheriting attributes.
Set<String> names = parent.getCascadedAttributeNames();
if (names != null && !names.isEmpty()) {
for (String name : names) {
Attribute attribute = parent.getCascadedAttribute(name);
Attribute destAttribute = getCascadedAttribute(name);
if (destAttribute == null) {
putAttribute(name, attribute, true);
} else if (attribute instanceof ListAttribute
&& destAttribute instanceof ListAttribute
&& ((ListAttribute) destAttribute).isInherit()) {
((ListAttribute) destAttribute).inherit((ListAttribute) attribute);
}
}
}
names = parent.getLocalAttributeNames();
if (names != null && !names.isEmpty()) {
for (String name : names) {
Attribute attribute = parent.getLocalAttribute(name);
Attribute destAttribute = getLocalAttribute(name);
if (destAttribute == null) {
putAttribute(name, attribute, false);
} else if (attribute instanceof ListAttribute
&& destAttribute instanceof ListAttribute
&& ((ListAttribute) destAttribute).isInherit()) {
((ListAttribute) destAttribute).inherit((ListAttribute) attribute);
}
}
}
}
}
/**
* Inherits the attribute context, inheriting, i.e. copying if not present,
* the attributes.
*
* @param parent The attribute context to inherit.
* @since 2.1.0
*/
public void inherit(BasicAttributeContext parent) {
// Set template, roles and preparer if not set.
inheritParentTemplateAttribute(parent.getTemplateAttribute());
if (preparer == null) {
preparer = parent.preparer;
}
// Sets attributes.
cascadedAttributes = addMissingAttributes(
((BasicAttributeContext) parent).cascadedAttributes,
cascadedAttributes);
attributes = addMissingAttributes(
((BasicAttributeContext) parent).attributes, attributes);
}
/**
* Add all attributes to this context.
* Copies all of the mappings from the specified attribute map to this context.
* New attribute mappings will replace any mappings that this context had for any of the keys
* currently in the specified attribute map.
*
* @param newAttributes Attributes to add.
* @since 2.1.0
*/
public void addAll(Map<String, Attribute> newAttributes) {
if (newAttributes == null) {
return;
}
if (attributes == null) {
attributes = new HashMap<String, Attribute>(newAttributes);
return;
}
attributes.putAll(newAttributes);
}
/**
* Add all missing attributes to this context.
* Copies all of the mappings from the specified attributes map to this context.
* New attribute mappings will be added only if they don't already exist in
* this context.
*
* @param defaultAttributes Attributes to add.
* @since 2.1.0
*/
public void addMissing(Map<String, Attribute> defaultAttributes) {
if (defaultAttributes == null) {
return;
}
if (attributes == null) {
attributes = new HashMap<String, Attribute>(defaultAttributes);
if (cascadedAttributes == null || cascadedAttributes.isEmpty()) {
return;
}
}
Set<Map.Entry<String, Attribute>> entries = defaultAttributes.entrySet();
for (Map.Entry<String, Attribute> entry : entries) {
String key = entry.getKey();
if (!attributes.containsKey(key)
&& (cascadedAttributes == null || cascadedAttributes
.containsKey(key))) {
attributes.put(entry.getKey(), entry.getValue());
}
}
}
/** {@inheritDoc} */
public Attribute getAttribute(String name) {
Attribute retValue = null;
if (attributes != null) {
retValue = attributes.get(name);
}
if (retValue == null && cascadedAttributes != null) {
retValue = cascadedAttributes.get(name);
}
return retValue;
}
/** {@inheritDoc} */
public Attribute getLocalAttribute(String name) {
if (attributes == null) {
return null;
}
return attributes.get(name);
}
/** {@inheritDoc} */
public Attribute getCascadedAttribute(String name) {
if (cascadedAttributes == null) {
return null;
}
return cascadedAttributes.get(name);
}
/** {@inheritDoc} */
public Iterator<String> getAttributeNames() {
Set<String> attributeSet = null;
if (attributes != null && !attributes.isEmpty()) {
attributeSet = new HashSet<String>(attributes
.keySet());
if (cascadedAttributes != null && !cascadedAttributes.isEmpty()) {
attributeSet.addAll(cascadedAttributes.keySet());
}
} else if (cascadedAttributes != null && !cascadedAttributes.isEmpty()) {
attributeSet = new HashSet<String>(cascadedAttributes.keySet());
}
if (attributeSet != null) {
return attributeSet.iterator();
} else {
return new ArrayList<String>().iterator();
}
}
/** {@inheritDoc} */
public Set<String> getLocalAttributeNames() {
if (attributes != null && !attributes.isEmpty()) {
return attributes.keySet();
} else {
return null;
}
}
/** {@inheritDoc} */
public Set<String> getCascadedAttributeNames() {
if (cascadedAttributes != null && !cascadedAttributes.isEmpty()) {
return cascadedAttributes.keySet();
} else {
return null;
}
}
/** {@inheritDoc} */
public void putAttribute(String name, Attribute value) {
if (attributes == null) {
attributes = new HashMap<String, Attribute>();
}
attributes.put(name, value);
}
/** {@inheritDoc} */
public void putAttribute(String name, Attribute value, boolean cascade) {
Map<String, Attribute> mapToUse;
if (cascade) {
if (cascadedAttributes == null) {
cascadedAttributes = new HashMap<String, Attribute>();
}
mapToUse = cascadedAttributes;
} else {
if (attributes == null) {
attributes = new HashMap<String, Attribute>();
}
mapToUse = attributes;
}
mapToUse.put(name, value);
}
/** {@inheritDoc} */
public void clear() {
templateAttribute = null;
preparer = null;
attributes.clear();
cascadedAttributes.clear();
}
/**
* Inherits the parent template attribute.
*
* @param parentTemplateAttribute The parent template attribute.
*/
private void inheritParentTemplateAttribute(
Attribute parentTemplateAttribute) {
if (parentTemplateAttribute != null) {
if (templateAttribute == null) {
templateAttribute = new Attribute(parentTemplateAttribute);
} else {
templateAttribute.inherit(parentTemplateAttribute);
}
}
}
/**
* Copies a BasicAttributeContext in an easier way.
*
* @param context The context to copy.
*/
private void copyBasicAttributeContext(BasicAttributeContext context) {
Attribute parentTemplateAttribute = context.getTemplateAttribute();
if (parentTemplateAttribute != null) {
this.templateAttribute = new Attribute(parentTemplateAttribute);
}
preparer = context.preparer;
if (context.attributes != null && !context.attributes.isEmpty()) {
attributes = deepCopyAttributeMap(context.attributes);
}
copyCascadedAttributes(context);
}
/**
* Copies the cascaded attributes to the current context.
*
* @param context The context to copy from.
*/
private void copyCascadedAttributes(BasicAttributeContext context) {
if (context.cascadedAttributes != null
&& !context.cascadedAttributes.isEmpty()) {
cascadedAttributes = deepCopyAttributeMap(context.cascadedAttributes);
}
}
/**
* Adds missing attributes to the destination map.
*
* @param source The source attribute map.
* @param destination The destination attribute map.
* @return The destination attribute map if not null, a new one otherwise.
*/
private Map<String, Attribute> addMissingAttributes(Map<String, Attribute> source,
Map<String, Attribute> destination) {
if (source != null && !source.isEmpty()) {
if (destination == null) {
destination = new HashMap<String, Attribute>();
}
for (Map.Entry<String, Attribute> entry : source.entrySet()) {
String key = entry.getKey();
Attribute destAttribute = destination.get(key);
if (destAttribute == null) {
destination.put(key, entry.getValue());
} else if (destAttribute instanceof ListAttribute
&& entry.getValue() instanceof ListAttribute
&& ((ListAttribute) destAttribute).isInherit()) {
((ListAttribute) destAttribute)
.inherit((ListAttribute) entry.getValue());
}
}
}
return destination;
}
/**
* Deep copies the attribute map, by creating clones (using copy
* constructors) of the attributes.
*
* @param attributes The attribute map to copy.
* @return The copied map.
*/
private Map<String, Attribute> deepCopyAttributeMap(
Map<String, Attribute> attributes) {
Map<String, Attribute> retValue = new HashMap<String, Attribute>(attributes.size());
for (Map.Entry<String, Attribute> entry : attributes.entrySet()) {
Attribute toCopy = entry.getValue();
if (toCopy != null) {
retValue.put(entry.getKey(), toCopy.clone());
}
}
return retValue;
}
}