package ecologylab.serialization.serializers.stringformats;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import ecologylab.serialization.ClassDescriptor;
import ecologylab.serialization.FieldDescriptor;
import ecologylab.serialization.FieldTypes;
import ecologylab.serialization.SIMPLTranslationException;
import ecologylab.serialization.SimplTypesScope;
import ecologylab.serialization.SimplTypesScope.GRAPH_SWITCH;
import ecologylab.serialization.TranslationContext;
import ecologylab.serialization.XMLTools;
import ecologylab.serialization.annotations.FieldUsage;
import ecologylab.serialization.formatenums.Format;
/**
* XML Specific serializer. contains functionalities specific to ouput syntax for XML from an objet
* model.
*
* @author nabeel
*/
public class XMLSerializer extends StringSerializer implements FieldTypes
{
private static final String START_CDATA = "<![CDATA[";
private static final String END_CDATA = "]]>";
private boolean isRoot = true;
public XMLSerializer()
{
}
@Override
public void serialize(Object object, Appendable appendable, TranslationContext translationContext)
throws SIMPLTranslationException
{
translationContext.resolveGraph(object);
ClassDescriptor<? extends FieldDescriptor> rootObjectClassDescriptor = ClassDescriptor
.getClassDescriptor(object.getClass());
try
{
serialize(object, rootObjectClassDescriptor.pseudoFieldDescriptor(), appendable,
translationContext);
}
catch (IOException e)
{
throw new SIMPLTranslationException("IO Exception occurred", e);
}
}
/**
*
* @param object
* @param rootObjectFieldDescriptor
* @param appendable
* @param translationContext
* @throws SIMPLTranslationException
* @throws IOException
*/
private void serialize(Object object, FieldDescriptor rootObjectFieldDescriptor,
Appendable appendable, TranslationContext translationContext)
throws SIMPLTranslationException, IOException
{
if (object == null)
return;
if (alreadySerialized(object, translationContext))
{
writeSimplRef(object, rootObjectFieldDescriptor, appendable, translationContext);
return;
}
translationContext.mapObject(object);
serializationPreHook(object, translationContext);
ClassDescriptor<? extends FieldDescriptor> rootObjectClassDescriptor = getClassDescriptor(object);
writeObjectStart(rootObjectFieldDescriptor, appendable);
serializeAttributes(object, appendable, translationContext, rootObjectClassDescriptor);
ArrayList<? extends FieldDescriptor> elementFieldDescriptors = rootObjectClassDescriptor
.elementFieldDescriptors();
boolean hasXMLText = rootObjectClassDescriptor.hasScalarFD();
boolean hasElements = elementFieldDescriptors.size() > 0;
if (!hasElements && !hasXMLText)
{
// close tag no more elements
writeCompleteClose(appendable);
}
else
{
writeClose(appendable);
if (hasXMLText)
{
writeValueAsText(object, rootObjectClassDescriptor.getScalarTextFD(), appendable);
}
serializeFields(object, appendable, translationContext, elementFieldDescriptors);
writeObjectClose(rootObjectFieldDescriptor, appendable);
}
serializationPostHook(object, translationContext);
}
/**
*
* @param object
* @param appendable
* @param translationContext
* @param rootObjectClassDescriptor
* @throws SIMPLTranslationException
* @throws IOException
*/
private void serializeAttributes(Object object, Appendable appendable,
TranslationContext translationContext,
ClassDescriptor<? extends FieldDescriptor> rootObjectClassDescriptor)
throws SIMPLTranslationException, IOException
{
ArrayList<? extends FieldDescriptor> attributeFieldDescriptors = rootObjectClassDescriptor
.attributeFieldDescriptors();
for (FieldDescriptor childFd : attributeFieldDescriptors)
{
try
{
writeValueAsAtrribute(object, childFd, appendable, translationContext);
}
catch (Exception ex)
{
throw new SIMPLTranslationException("serialize for attribute " + object, ex);
}
}
if (SimplTypesScope.graphSwitch == GRAPH_SWITCH.ON)
{
if (translationContext.needsHashCode(object))
{
writeSimplIdAttribute(object, appendable, translationContext);
}
if (isRoot && translationContext.isGraph())
{
writeSimplNameSpace(appendable);
isRoot = false;
}
}
}
/**
*
* @param object
* @param appendable
* @param translationContext
* @param elementFieldDescriptors
* @throws SIMPLTranslationException
* @throws IOException
*/
private void serializeFields(Object object, Appendable appendable,
TranslationContext translationContext,
ArrayList<? extends FieldDescriptor> elementFieldDescriptors)
throws SIMPLTranslationException, IOException
{
for (FieldDescriptor childFd : elementFieldDescriptors)
{
switch (childFd.getType())
{
case SCALAR:
writeValueAsLeaf(object, childFd, appendable, translationContext);
break;
case COMPOSITE_ELEMENT:
Object compositeObject = childFd.getValue(object);
if (compositeObject != null)
{
FieldDescriptor compositeObjectFieldDescriptor = childFd.isPolymorphic() ? getClassDescriptor(
compositeObject).pseudoFieldDescriptor()
: childFd;
writeWrap(childFd, appendable, false);
serialize(compositeObject, compositeObjectFieldDescriptor, appendable, translationContext);
writeWrap(childFd, appendable, true);
}
break;
case COLLECTION_SCALAR:
case MAP_SCALAR:
Object scalarCollectionObject = childFd.getValue(object);
Collection<?> scalarCollection = XMLTools.getCollection(scalarCollectionObject);
if (scalarCollection != null && scalarCollection.size() > 0)
{
writeWrap(childFd, appendable, false);
for (Object collectionScalar : scalarCollection)
{
writeScalarCollectionLeaf(collectionScalar, childFd, appendable, translationContext);
}
writeWrap(childFd, appendable, true);
}
break;
case COLLECTION_ELEMENT:
case MAP_ELEMENT:
Object compositeCollectionObject = childFd.getValue(object);
Collection<?> compositeCollection = XMLTools.getCollection(compositeCollectionObject);
if (compositeCollection != null && compositeCollection.size() > 0)
{
writeWrap(childFd, appendable, false);
for (Object collectionComposite : compositeCollection)
{
FieldDescriptor collectionObjectFieldDescriptor = childFd.isPolymorphic() ? getClassDescriptor(
collectionComposite).pseudoFieldDescriptor()
: childFd;
serialize(collectionComposite, collectionObjectFieldDescriptor, appendable,
translationContext);
}
writeWrap(childFd, appendable, true);
}
break;
}
}
}
/**
*
* @param object
* @param fd
* @param appendable
* @throws IOException
*/
private void writeSimplRef(Object object, FieldDescriptor fd, Appendable appendable, TranslationContext translationContext)
throws IOException
{
writeObjectStart(fd, appendable);
writeSimplRefAttribute(object, appendable, translationContext);
writeCompleteClose(appendable);
}
/**
*
* @param fd
* @param appendable
* @throws IOException
*/
private void writeObjectStart(FieldDescriptor fd, Appendable appendable) throws IOException
{
appendable.append('<').append(fd.elementStart());
}
/**
*
* @param fd
* @param appendable
* @throws IOException
*/
private void writeObjectClose(FieldDescriptor fd, Appendable appendable) throws IOException
{
appendable.append('<').append('/').append(fd.elementStart()).append('>');
}
/**
*
* @param appendable
* @throws IOException
*/
private void writeCompleteClose(Appendable appendable) throws IOException
{
appendable.append('/').append('>');
}
/**
*
* @param fd
* @param appendable
* @param close
* @throws IOException
*/
private void writeWrap(FieldDescriptor fd, Appendable appendable, boolean close)
throws IOException
{
if (fd.isWrapped())
{
appendable.append('<');
if (close)
appendable.append('/');
appendable.append(fd.getTagName()).append('>');
}
}
/**
*
* @param object
* @param fd
* @param appendable
* @param translationContext
* @throws SIMPLTranslationException
* @throws IOException
*/
private void writeValueAsLeaf(Object object, FieldDescriptor fd, Appendable appendable,
TranslationContext translationContext) throws SIMPLTranslationException, IOException
{
if (!fd.isDefaultValueFromContext(object))
{
appendable.append('<').append(fd.elementStart()).append('>');
fd.appendValue(appendable, object, translationContext, Format.XML);
appendable.append('<').append('/').append(fd.elementStart()).append('>');
}
}
/**
*
* @param object
* @param fd
* @param appendable
* @param translationContext
* @throws SIMPLTranslationException
* @throws IOException
*/
private void writeScalarCollectionLeaf(Object object, FieldDescriptor fd, Appendable appendable,
TranslationContext translationContext) throws SIMPLTranslationException, IOException
{
appendable.append('<').append(fd.elementStart()).append('>');
fd.appendCollectionScalarValue(appendable, object, translationContext, Format.XML);
appendable.append('<').append('/').append(fd.elementStart()).append('>');
}
/**
*
* @param object
* @param fd
* @param appendable
* @throws SIMPLTranslationException
* @throws IOException
*/
private void writeValueAsText(Object object, FieldDescriptor fd, Appendable appendable)
throws SIMPLTranslationException, IOException
{
if (!fd.isDefaultValueFromContext(object))
{
if (fd.isCDATA())
appendable.append(START_CDATA);
fd.appendValue(appendable, object, null, Format.XML);
if (fd.isCDATA())
appendable.append(END_CDATA);
}
}
/**
*
* @param appendable
* @throws IOException
*/
private void writeClose(Appendable appendable) throws IOException
{
appendable.append('>');
}
/**
*
* @param object
* @param fd
* @param appendable
* @param translationContext
* @throws SIMPLTranslationException
* @throws IOException
*/
private void writeValueAsAtrribute(Object object, FieldDescriptor fd, Appendable appendable,
TranslationContext translationContext) throws SIMPLTranslationException, IOException
{
if (object != null)
{
if (!fd.isDefaultValueFromContext(object))
{
appendable.append(' ');
appendable.append(fd.getTagName());
appendable.append('=');
appendable.append('"');
fd.appendValue(appendable, object, translationContext, Format.XML);
appendable.append('"');
}
}
}
/**
*
* @param appendable
* @throws IOException
*/
private void writeSimplNameSpace(Appendable appendable) throws IOException
{
appendable.append(TranslationContext.SIMPL_NAMESPACE);
}
/**
*
* @param object
* @param appendable
* @throws IOException
*/
private void writeSimplRefAttribute(Object object, Appendable appendable, TranslationContext translationContext) throws IOException
{
appendable.append(' ');
appendable.append(TranslationContext.SIMPL_REF);
appendable.append('=');
appendable.append('"');
appendable.append(translationContext.getSimplId(object));
appendable.append('"');
}
/**
*
* @param object
* @param appendable
* @throws IOException
*/
private void writeSimplIdAttribute(Object object, Appendable appendable, TranslationContext translationContext) throws IOException
{
appendable.append(' ');
appendable.append(TranslationContext.SIMPL_ID);
appendable.append('=');
appendable.append('"');
appendable.append(translationContext.getSimplId(object));
appendable.append('"');
}
}