/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt 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.jboss.metatype.plugins.types;
import java.util.Collections;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeMap;
import org.jboss.metatype.api.types.AbstractMetaType;
import org.jboss.metatype.api.types.CompositeMetaType;
import org.jboss.metatype.api.types.MetaType;
import org.jboss.metatype.api.values.CompositeValue;
/**
* ImmutableCompositeMetaType.
*
* @author <a href="adrian@jboss.com">Adrian Brock</a>
* @version $Revision: 1.1 $
*/
public abstract class AbstractCompositeMetaType extends AbstractMetaType implements CompositeMetaType
{
/** The serialVersionUID */
private static final long serialVersionUID = -7421421680257307598L;
/** Item names to descriptions */
private TreeMap<String, String> nameToDescription;
/** Item names to meta types */
private TreeMap<String, MetaType> nameToType;
/** The keys */
private Set<String> keys;
/**
* Construct a composite meta type. The parameters are checked for validity.<p>
*
* The three arrays are internally copied. Future changes to these
* arrays do not alter the composite type.<p>
*
* getClassName() returns {@link CompositeValue}
*
* @param typeName the name of the composite type, cannot be null or empty
* @param description the human readable description of the composite type, cannot be null or empty
* @param itemNames the names of the items described by this type. Cannot
* be null, must contain at least one element, the elements cannot
* be null or empty. The order of the items is unimportant when
* determining equality.
* @param itemDescriptions the human readable descriptions of the items
* in the same order as the itemNames, cannot be null must have the
* same number of elements as the itemNames. The elements cannot
* be null or empty.
* @param itemTypes the MetaTypes of the items in the same order as the
* item names, cannot be null must have the
* same number of elements as the itemNames. The elements cannot
* be null.
* @param ignoreItems whether to ignore items
* @exception IllegalArgumentException when a parameter does not match
* what is described above or when itemNames contains a duplicate name.
* The names are case sensitive, leading and trailing whitespace
* is ignored.
*/
protected AbstractCompositeMetaType(String typeName, String description, String[] itemNames, String[] itemDescriptions, MetaType[] itemTypes, boolean ignoreItems)
{
super(CompositeValue.class.getName(), typeName, description);
if (ignoreItems)
{
nameToDescription = new TreeMap<String, String>();
nameToType = new TreeMap<String, MetaType>();
return;
}
if (itemNames == null || itemNames.length == 0)
throw new IllegalArgumentException("null or empty itemNames");
if (itemDescriptions == null || itemDescriptions.length == 0)
throw new IllegalArgumentException("null or empty itemDescriptions");
if (itemTypes == null || itemTypes.length == 0)
throw new IllegalArgumentException("null or empty itemTypes");
if (itemNames.length != itemDescriptions.length)
throw new IllegalArgumentException("wrong number of itemDescriptions");
if (itemNames.length != itemTypes.length)
throw new IllegalArgumentException("wrong number of itemTypes");
nameToDescription = new TreeMap<String, String>();
nameToType = new TreeMap<String, MetaType>();
for (int i = 0; i < itemNames.length; ++i)
{
try
{
addItem(itemNames[i], itemDescriptions[i], itemTypes[i]);
}
catch (IllegalArgumentException e)
{
IllegalArgumentException e1 = new IllegalArgumentException(e.getMessage() + " for item " + i);
e1.setStackTrace(e.getStackTrace());
throw e1;
}
}
}
/**
* Construct a composite meta type with no items.
*
* @param typeName the name of the composite type, cannot be null or empty
* @param description the human readable description of the composite type, cannot be null or empty
* @throws IllegalArgumentException when a parameter does not match what is described above.
*/
protected AbstractCompositeMetaType(String typeName, String description)
{
this(typeName, description, null, null, null, true);
}
/**
* Set the keys
*
* @param keySet the key set
*/
protected void setKeys(Set<String> keySet)
{
if (keySet != null)
{
for (String key : keySet)
{
if (containsItem(key) == false)
throw new IllegalArgumentException("Key " + key + " is not an item " + itemSet());
}
}
keys = keySet;
}
/**
* Add an item
*
* @param itemName the item name
* @param itemDescription the item description
* @param itemType the item type
* @throws IllegalArgumentException for a null or empty item name, description or type or duplicate item
*/
protected void addItem(String itemName, String itemDescription, MetaType itemType)
{
if (itemName == null)
throw new IllegalArgumentException("null item name");
itemName = itemName.trim();
if (itemName.length() == 0)
throw new IllegalArgumentException("empty item name");
if (nameToDescription.containsKey(itemName))
throw new IllegalArgumentException("duplicate item name " + itemName);
if (itemDescription == null)
throw new IllegalArgumentException("null item description");
itemDescription = itemDescription.trim();
if (itemDescription.length() == 0)
throw new IllegalArgumentException("empty item description");
if (itemType == null)
throw new IllegalArgumentException("null item type");
nameToDescription.put(itemName, itemDescription);
nameToType.put(itemName, itemType);
}
public boolean containsItem(String itemName)
{
if (itemName == null)
return false;
return nameToDescription.containsKey(itemName);
}
public String getDescription(String itemName)
{
if (itemName == null)
return null;
return nameToDescription.get(itemName);
}
@Override
public boolean isComposite()
{
return true;
}
public MetaType getType(String itemName)
{
if (itemName == null)
return null;
return nameToType.get(itemName);
}
public Set<String> itemSet()
{
return Collections.unmodifiableSet(nameToDescription.keySet());
}
public Set<String> keySet()
{
if (keys == null)
return itemSet();
return Collections.unmodifiableSet(keys);
}
@Override
public boolean isValue(Object obj)
{
if (obj == null || obj instanceof CompositeValue == false)
return false;
return equalsImpl(((CompositeValue) obj).getMetaType());
}
/**
* Implementation of equals
*
* @param obj the object to check
* @return true when equals false otherwise
*/
protected boolean equalsImpl(Object obj)
{
if (this == obj)
return true;
if (obj == null || obj instanceof CompositeMetaType == false)
return false;
CompositeMetaType other = (CompositeMetaType) obj;
if (this.getTypeName().equals(other.getTypeName()) == false)
return false;
Iterator<String> thisNames = this.keySet().iterator();
Iterator<String> otherNames = other.keySet().iterator();
while(thisNames.hasNext() && otherNames.hasNext())
{
String thisName = thisNames.next();
String otherName = otherNames.next();
if (thisName.equals(otherName) == false)
return false;
if (this.getType(thisName).equals(other.getType(otherName)) == false)
return false;
}
if (thisNames.hasNext() || otherNames.hasNext())
return false;
return true;
}
/**
* Hashcode implementation
*
* @return the hash code
*/
protected int hashCodeImpl()
{
int hashCode = getTypeName().hashCode();
for (Object o : nameToType.values())
hashCode += o.hashCode();
for (Object o : keySet())
hashCode += o.hashCode();
return hashCode;
}
@Override
public String toString()
{
StringBuilder buffer = new StringBuilder(getClass().getSimpleName());
buffer.append('{').append(getTypeName());
Iterator<String> thisNames = itemSet().iterator();
if (thisNames.hasNext())
{
buffer.append(" items=");
while(thisNames.hasNext())
{
String thisName = thisNames.next();
buffer.append("[");
buffer.append("name=");
buffer.append(thisName);
buffer.append(" type=");
buffer.append(getType(thisName).getTypeName());
buffer.append("]");
if (thisNames.hasNext())
buffer.append(", ");
}
}
buffer.append('}');
return buffer.toString();
}
}