package de.jpaw.bonaparte.refsw; import java.io.IOException; import java.util.Map; import de.jpaw.bonaparte.core.BonaCustom; import de.jpaw.bonaparte.core.CompactByteArrayComposer; import de.jpaw.bonaparte.pojos.api.AbstractRef; import de.jpaw.bonaparte.pojos.api.VoidRef; import de.jpaw.bonaparte.pojos.meta.ClassDefinition; import de.jpaw.bonaparte.pojos.meta.FieldDefinition; import de.jpaw.bonaparte.pojos.meta.ObjectReference; import de.jpaw.util.ApplicationException; import de.jpaw.util.ByteBuilder; /** A composer of the compact format family, using classIds instead of names and replacing references to other classes by the key. */ public class ReferencingComposer extends CompactByteArrayComposer { private static final AbstractRef DOES_NOT_MATCH_ANY = new VoidRef(); private final Map<ClassDefinition,RefResolver<AbstractRef, ?, ?>> resolvers; private AbstractRef excludedObject = DOES_NOT_MATCH_ANY; // an object not to replace, usually the outer one, in case the resolver map is created as a static object public ReferencingComposer(ByteBuilder out, Map<ClassDefinition,RefResolver<AbstractRef, ?, ?>> resolvers) { super(out, true); this.resolvers = resolvers; } public void excludeObject(AbstractRef obj) { excludedObject = obj == null ? DOES_NOT_MATCH_ANY : obj; } protected RefResolver<AbstractRef, ?, ?> getReferencedResolver(ObjectReference di) { return di.getLowerBound() == null ? null : resolvers.get(di.getLowerBound()); } @Override public void startMap(FieldDefinition di, int currentMembers) { out.writeByte(MAP_BEGIN); try { // write the map count or an indicator if the object is external (because size could change independently then) // important: the provided map must be identical for composing and parsing if (di instanceof ObjectReference && getReferencedResolver((ObjectReference)di) != null) { intOut(COLLECTION_COUNT_REF); } else { intOut(currentMembers); } } catch (IOException e) { // IOException from ByteArray operation??? throw new RuntimeException(e); } } @Override public void startArray(FieldDefinition di, int currentMembers, int sizeOfElement) { out.writeByte(ARRAY_BEGIN); try { // write the array count or an indicator if the object is external (because size could change independently then) // important: the provided map must be identical for composing and parsing if (di instanceof ObjectReference && getReferencedResolver((ObjectReference)di) != null) { intOut(COLLECTION_COUNT_REF); } else { intOut(currentMembers); } } catch (IOException e) { // IOException from ByteArray operation??? throw new RuntimeException(e); } } @Override public void addField(ObjectReference di, BonaCustom obj) { final RefResolver<AbstractRef, ?, ?> r = getReferencedResolver(di); if (r == null || obj == null || obj == excludedObject) { super.addField(di, obj); } else { // this is an object to replace by its reference AbstractRef refobj = (AbstractRef)obj; // see if the reference is contained already long ref = refobj.ret$RefP(); if (ref <= 0) { // contained references we have to resolve first try { ref = r.getRef(refobj).longValue(); } catch (ApplicationException e) { throw new RuntimeException(e); } } try { longOut(ref); } catch (IOException e) { // IOException from ByteArray operation??? throw new RuntimeException(e); } } } }