/*
* $Id: BasicAttributeContext.java 943645 2010-05-12 19:33:19Z apetrelli $
*
* 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 static org.apache.tiles.
CompareUtil.*;
import java.io.
Serializable;
import java.util.
HashMap;
import java.util.
Map;
import java.util.
Set;
/**
* Basic implementation for <code>AttributeContext</code>.
*
* @version $Rev: 943645 $ $Date: 2010-05-13 05:33:19 +1000 (Thu, 13 May 2010) $
* @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() {
}
/**
* 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>();
Set<
String>
parentAttributeNames =
context.
getLocalAttributeNames();
if (
parentAttributeNames != null) {
for (
String name :
parentAttributeNames) {
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>();
Set<
String>
parentAttributeNames =
context.
getCascadedAttributeNames();
if (
parentAttributeNames != null) {
for (
String name :
parentAttributeNames) {
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(
parent.
cascadedAttributes,
cascadedAttributes);
attributes =
addMissingAttributes(
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>();
if (
cascadedAttributes == null ||
cascadedAttributes.
isEmpty()) {
attributes.
putAll(
defaultAttributes);
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
Set<
String>
getLocalAttributeNames() {
if (
attributes != null && !
attributes.
isEmpty()) {
return
attributes.
keySet();
}
return null;
}
/** {@inheritDoc} */
public
Set<
String>
getCascadedAttributeNames() {
if (
cascadedAttributes != null && !
cascadedAttributes.
isEmpty()) {
return
cascadedAttributes.
keySet();
}
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();
}
/** {@inheritDoc} */
@
Override
public boolean
equals(
Object obj) {
BasicAttributeContext bac = (
BasicAttributeContext)
obj;
return
nullSafeEquals(
templateAttribute,
bac.
templateAttribute)
&&
nullSafeEquals(
preparer,
bac.
preparer)
&&
nullSafeEquals(
attributes,
bac.
attributes)
&&
nullSafeEquals(
cascadedAttributes,
bac.
cascadedAttributes);
}
/** {@inheritDoc} */
@
Override
public int
hashCode() {
return
nullSafeHashCode(
templateAttribute) +
nullSafeHashCode(
preparer)
+
nullSafeHashCode(
attributes)
+
nullSafeHashCode(
cascadedAttributes);
}
/**
* 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;
}
}