com.tangosol.run.xml.ArrayAdapter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of coherence Show documentation
Show all versions of coherence Show documentation
Oracle Coherence Community Edition
/*
* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
package com.tangosol.run.xml;
import com.tangosol.util.ExternalizableHelper;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
/**
* An ArrayAdapter supports properties of Java array types (not including
* arrays of primitive types).
*
* {@code
* <property>
* <name>People</name>
* <xml-name>people</xml-name> <!-- defaults to <name> -->
* <type>com...Person[]</type> <!-- defaults via reflection -->
* <class>com...Person[]</class> <!-- defaults from <type> -->
* <sparse>true</sparse> <!-- defaults to false -->
* <empty-is-null>true</empty-is-null> <!-- defaults to false -->
* <element> <!-- optional -->
* <xml-name>person</xml-name> <!-- optional, nests the elements -->
* <type>com...Person</type> <!-- auto-set from <property><class> -->
* <adapter>...</adapter> <!-- optional -->
* <...> <!-- for the type-specific adapter -->
* </element>
* </property>
* }
*
* Example of collection nested within collection tags:
*
* {@code
* <doc>
* <people>
* <person>
* <...>
* </person>
* <person>
* <...>
* </person>
* ...
* </people>
* </doc>
* }
*
* @version 1.00 2001.03.18
* @author cp
*/
public class ArrayAdapter
extends IterableAdapter
{
// ----- constructors ---------------------------------------------------
/**
* Construct a ArrayAdapter.
*
* @param infoBean BeanInfo for a bean containing this property
* @param clzType the type of the property
* @param sName the property name
* @param sXml the XML tag name
* @param xml additional XML information
*/
public ArrayAdapter(XmlBean.BeanInfo infoBean, Class clzType, String sName, String sXml, XmlElement xml)
{
super(infoBean, clzType, sName, sXml, xml);
azzert(Object[].class.isAssignableFrom(clzType));
// determine the array class used
Class clzArray = getType();
XmlValue xmlClz = xml.getElement("class");
if (xmlClz != null)
{
clzArray = infoBean.resolveClass(xmlClz.getString());
}
// determine the element type used
Class clzElement = clzArray.getComponentType();
azzert(clzElement != null);
m_clzElement = clzElement;
XmlElement xmlElement = xml.ensureElement("element");
// copy type to the type of the element
xmlElement.ensureElement("type").setString(clzElement.getName());
m_adapterElement = findAdapter(infoBean, xmlElement);
}
// ----- Object method helpers ------------------------------------------
/**
* compute a hash code for the passed object.
*
* @param o the object to compute a hash code for
*
* @return an integer hash code
*/
public int hash(Object o)
{
Object[] ao = (Object[]) o;
if (ao == null || ao.length == 0)
{
return 0;
}
int n = 0;
PropertyAdapter adapterElement = m_adapterElement;
for (int i = 0, c = ao.length; i < c; ++i)
{
Object oElement = ao[i];
if (oElement != null)
{
n ^= adapterElement.hash(oElement);
}
}
return n;
}
/**
* Compare the two passed objects for equality.
*
* @param o1 the first object
* @param o2 the second object
*
* @return true if the two objects are equal
*/
public boolean equalsValue(Object o1, Object o2)
{
if (o1 == o2)
{
return true;
}
if (o1 == null || o2 == null)
{
return false;
}
Object[] ao1 = (Object[]) o1;
Object[] ao2 = (Object[]) o2;
if (ao1.length != ao2.length)
{
return false;
}
PropertyAdapter adapterElement = m_adapterElement;
for (int i = 0, c = ao1.length; i < c; ++i)
{
Object oElement1 = ao1[i];
Object oElement2 = ao2[i];
if (!adapterElement.equalsValue(oElement1, oElement2))
{
return false;
}
}
return true;
}
/**
* Make a clone of the passed object.
*
* @param o the object to clone
*
* @return a clone of the passed object
*/
public Object clone(Object o)
{
if (o == null)
{
return null;
}
Object[] aoOld = (Object[]) o;
int c = aoOld.length;
if (c == 0)
{
return aoOld;
}
Object[] aoNew = (Object[]) aoOld.clone();
PropertyAdapter adapterElement = m_adapterElement;
for (int i = 0; i < c; ++i)
{
Object oOld = aoNew[i];
if (oOld != null)
{
aoNew[i] = adapterElement.clone(oOld);
}
}
return aoNew;
}
// ----- XmlSerializable helpers ----------------------------------------
/**
* @param xml the XML element containing the XML elements to deserialize
* from
*
* @return the object deserialized from the XML (not null)
*/
protected Object readElements(XmlElement xml)
{
PropertyAdapter adapterElement = m_adapterElement;
Iterator iter = adapterElement.isAnonymous() ?
xml.getElementList().iterator() :
XmlHelper.getElements(xml, getElementName(), adapterElement.getNamespaceUri());
if (isSparse())
{
int c = xml.getSafeAttribute("length").getInt();
Object[] ao = (Object[]) Array.newInstance(m_clzElement, c);
while (iter.hasNext())
{
XmlElement xmlElement = (XmlElement) iter.next();
Object oElement = adapterElement.fromXml(xmlElement);
XmlValue attrId = xmlElement.getAttribute("id");
if (attrId == null)
{
throw new IllegalArgumentException("Element " + xmlElement.getName()
+ " is missing the required \"id\" attribute");
}
ao[attrId.getInt(-1)] = oElement;
}
return ao;
}
else
{
List list = new ArrayList();
while (iter.hasNext())
{
XmlElement xmlElement = (XmlElement) iter.next();
Object oElement = adapterElement.fromXml(xmlElement);
list.add(oElement);
}
return list.isEmpty() ? null :
list.toArray((Object[]) Array.newInstance(m_clzElement, list.size()));
}
}
/**
* @param xml the XML element to which the iterable elements are written
* @param o the object to serialize (not null)
*/
protected void writeElements(XmlElement xml, Object o)
{
PropertyAdapter adapterElement = m_adapterElement;
boolean fAnonymous = adapterElement.isAnonymous();
String sNmsPrefix = null;
String sElement = null;
if (!fAnonymous)
{
sNmsPrefix = adapterElement.getNamespacePrefix();
sElement = XmlHelper.getUniversalName(getElementName(), sNmsPrefix);
}
Object[] ao = (Object[]) o;
int c = ao.length;
boolean fSparse = isSparse();
if (fSparse)
{
xml.addAttribute("length").setInt(c);
}
List list = xml.getElementList();
for (int i = 0; i < c; ++i)
{
Object oElement = ao[i];
if (oElement == null)
{
if (!fSparse && !fAnonymous)
{
// add a place-holder for the null element
xml.addElement(sElement);
}
}
else
{
XmlElement xmlElement = adapterElement.toXml(oElement);
if (fAnonymous)
{
List listElement = xmlElement.getElementList();
int cElements = listElement.size();
if (cElements == 1)
{
list.add(listElement.get(0));
}
else if (cElements > 1)
{
throw new IllegalStateException("Too many elements: " + xmlElement +
"\nadapter=" + adapterElement);
}
}
else
{
xmlElement.setName(sElement);
if (fSparse)
{
xmlElement.addAttribute("id").setInt(i);
}
list.add(xmlElement);
}
}
}
/*
if (sNmsPrefix != null)
{
XmlHelper.ensureNamespace(xml, sNmsPrefix, adapterElement.getNamespaceUri());
XmlHelper.purgeChildrenNamespace(xml);
}
*/
}
// ----- ExternalizableLite helpers -------------------------------------
/**
* Read a value from the passed DataInput object.
*
* @param in the DataInput stream to read property data from
*
* @return the data read from the DataInput; never null
*
* @exception IOException if an I/O exception occurs
*/
public Object readExternal(DataInput in)
throws IOException
{
// "in" contains an array size and, for each element, a non-null
// indicator and (if non-null) the object
int c = readInt(in);
Object[] ao = c < CHUNK_THRESHOLD >> 4
? readArray(in, c)
: readLargeArray(in, c);
return ao;
}
/**
* Read an array of specified length from the passed DataInput object.
*
* @param in the DataInput stream to read property data from
*
* @return the data read from the DataInput; never null
*
* @exception IOException if an I/O exception occurs
*/
protected Object[] readArray(DataInput in, int c)
throws IOException
{
Object[] ao = (Object[]) Array.newInstance(m_clzElement, c);
PropertyAdapter adapter = m_adapterElement;
for (int i = 0; i < c; ++i)
{
if (in.readBoolean())
{
ao[i] = adapter.readExternal(in);
}
}
return ao;
}
/**
* Read an array of property data with length larger than
* {@link #CHUNK_THRESHOLD} {@literal >>} 4.
*
* @param in the DataInput stream to read property data from
*
* @return the data read from the DataInput; never null
*
* @exception IOException if an I/O exception occurs
*/
protected Object[] readLargeArray(DataInput in, int c)
throws IOException
{
int cBatchMax = CHUNK_SIZE >> 4;
int cBatch = c / cBatchMax + 1;
Object[] aMerged = null;
int cRead = 0;
int cAllocate = cBatchMax;
Object[] ao;
for (int i = 0; i < cBatch && cRead < c; i++)
{
ao = readArray(in, cAllocate);
aMerged = mergeArray(aMerged, ao);
cRead += ao.length;
cAllocate = Math.min(c - cRead, cBatchMax);
}
return aMerged;
}
/**
* Write the specified data to the passed DataOutput object.
*
* @param out the DataOutput stream to write to
* @param o the data to write to the DataOutput; never null
*
* @exception IOException if an I/O exception occurs
*/
public void writeExternal(DataOutput out, Object o)
throws IOException
{
Object[] ao = (Object[]) o;
int c = ao.length;
writeInt(out, c);
PropertyAdapter adapter = m_adapterElement;
for (int i = 0; i < c; ++i)
{
Object oElement = ao[i];
boolean fExists = (oElement != null);
out.writeBoolean(fExists);
if (fExists)
{
adapter.writeExternal(out, oElement);
}
}
}
// ----- PropertyAdapter methods ----------------------------------------
/**
* Determine if the specified value is empty.
*
* @param o the value
*
* @return true if the object is considered to be empty for persistence
* and XML-generation purposes
*/
public boolean isEmpty(Object o)
{
return o == null || isEmptyIsNull() && ((Object[]) o).length == 0;
}
// ----- data members ---------------------------------------------------
/**
* The element type of the array.
*/
private Class m_clzElement;
/**
* The adapter for the elements in the array.
*/
private PropertyAdapter m_adapterElement;
}