/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.portal.mop.navigation;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.gatein.common.logging.Logger;
import org.gatein.common.logging.LoggerFactory;
import org.gatein.mop.api.Attributes;
/**
*
* Immutable attributes. The underlying
* {@link Map} is granted:
* <ol>
* <li>
* Not to be changed once the given {@link AttributesState} is created.
* <li>
* Not to be exposed to any code able to change it outside this class.
* </ol>
* How to create:
* <pre>AttributesState attributesState = new AttributesState.Builder()
* .attribute("key1", "value1")
* .attribute("key2", "value2")
* .build();</pre>
*
* @author <a href="mailto:ppalaga@redhat.com">Peter Palaga</a>
*
*/
public class AttributesState implements Serializable, Map<String, String> {
private static final Logger log = LoggerFactory.getLogger(AttributesState.class);
public static class Builder {
private Map<String, String> map;
public Builder() {
}
public Builder(Map<? extends String, ? extends String> attributes) {
super();
attributes(attributes);
}
public AttributesState.Builder attribute(String key, String value) {
if (value == null) {
if (map != null) {
/* here we follow what is in SimpleAttributes */
map.remove(key);
}
} else {
if (map == null) {
map = new HashMap<String, String>();
}
map.put(key, value);
}
return this;
}
public AttributesState.Builder attributes(Map<? extends String, ? extends String> attributes) {
if (attributes != null) {
if (attributes.size() > 0) {
if (map == null) {
int attributesSize = attributes.size();
map = new HashMap<String, String>(attributesSize + attributesSize / 2);
}
for (Map.Entry<? extends String, ? extends String> en : attributes.entrySet()) {
if (en.getValue() == null) {
/* here we follow what is in SimpleAttributes */
map.remove(en.getKey());
} else {
map.put(en.getKey(), en.getValue());
}
}
}
}
return this;
}
public AttributesState.Builder attributes(String filterPrefix, Attributes attributes) {
if (attributes == null) {
if (map != null) {
map.clear();
}
} else {
Set<String> keys = attributes.getKeys();
if (keys.size() > 0) {
if (map == null) {
int attributesSize = keys.size();
map = new HashMap<String, String>(attributesSize + attributesSize / 2);
}
for (String key : attributes.getKeys()) {
if (key.startsWith(filterPrefix)) {
try {
String value = attributes.getString(key);
if (value == null) {
map.remove(key);
} else {
map.put(key, value);
}
} catch (ClassCastException e) {
log.warn("Could not cast value of attribute '"+ key +"' to String.", e);
}
}
}
}
}
return this;
}
public AttributesState build() {
return new AttributesState(map);
}
}
public static final AttributesState EMPTY = new AttributesState(null);
private final Map<String, String> map;
/**
* @param map
*/
private AttributesState(Map<String, String> map) {
if (map == null) {
this.map = Collections.emptyMap();
} else {
this.map = Collections.unmodifiableMap(map);
}
}
/**
*
* @see java.util.Map#clear()
*/
public void clear() {
map.clear();
}
/**
* @param key
* @return
* @see java.util.Map#containsKey(java.lang.Object)
*/
public boolean containsKey(Object key) {
return map.containsKey(key);
}
/**
* @param value
* @return
* @see java.util.Map#containsValue(java.lang.Object)
*/
public boolean containsValue(Object value) {
return map.containsValue(value);
}
/**
* @return
* @see java.util.Map#entrySet()
*/
public Set<java.util.Map.Entry<String, String>> entrySet() {
return map.entrySet();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj == null) {
return false;
} else if (obj.getClass() == this.getClass()) {
AttributesState other = (AttributesState) obj;
return other.map == this.map || (other.map != null && other.map.equals(this.map));
}
return false;
}
/**
* @param key
* @return
* @see java.util.Map#get(java.lang.Object)
*/
public String get(Object key) {
return map.get(key);
}
protected Object get(String name) {
return map.get(name);
}
@Override
public int hashCode() {
return map.hashCode();
}
/**
* @return
* @see java.util.Map#isEmpty()
*/
public boolean isEmpty() {
return map.isEmpty();
}
/**
* @return
* @see java.util.Map#keySet()
*/
public Set<String> keySet() {
return map.keySet();
}
/**
* @param key
* @param value
* @return
* @see java.util.Map#put(java.lang.Object, java.lang.Object)
*/
public String put(String key, String value) {
return map.put(key, value);
}
/**
* @param m
* @see java.util.Map#putAll(java.util.Map)
*/
public void putAll(Map<? extends String, ? extends String> m) {
map.putAll(m);
}
/**
* @param key
* @return
* @see java.util.Map#remove(java.lang.Object)
*/
public String remove(Object key) {
return map.remove(key);
}
/**
* @return
* @see java.util.Map#size()
*/
public int size() {
return map.size();
}
@Override
public String toString() {
return map.toString();
}
/**
* @return
* @see java.util.Map#values()
*/
public Collection<String> values() {
return map.values();
}
/**
* Synchronises entries from the given {@link AttributesState} to the given
* {@link Attributes}. Keys from {@link AttributesState} entries will prefixed with
* the given {@code prefix} and then put to the given {@link Attributes}.
* All keys not present in the given {@link AttributesState} will be removed from the given
* {@link Attributes}.
*
* @param src sync from
* @param prefix used to prefix keys from {@code attributesState} in {@code attrs}
* @param target sync to
*/
public static void sync(AttributesState src, String prefix, Attributes target) {
if (src != null) {
/* remove the missing ones first */
/* copy to avoid a concurrent modification exception */
Set<String> targetKeys = new HashSet<String>(target.getKeys());
for (String key : targetKeys) {
if (key.startsWith(prefix)
&& !src.containsKey(key.substring(prefix.length()))) {
target.setObject(key, null);
}
}
for (Map.Entry<String, String> stateEntry : src.entrySet()) {
target.setObject(prefix + stateEntry.getKey(), stateEntry.getValue());
}
} else {
/* attributesState is null - remove all prefixed */
/* copy to avoid a concurrent modification exception */
Set<String> targetKeys = new HashSet<String>(target.getKeys());
for (String key : targetKeys) {
if (key.startsWith(prefix)) {
target.setObject(key, null);
}
}
}
}
}