package de.jpaw.bonaparte.refsw; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import de.jpaw.bonaparte.core.BonaPortable; import de.jpaw.bonaparte.core.CompactByteArrayParser; import de.jpaw.bonaparte.core.MessageParserException; import de.jpaw.bonaparte.pojos.api.AbstractRef; 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; public class ReferencingParser extends CompactByteArrayParser { private static final Logger LOGGER = LoggerFactory.getLogger(ReferencingParser.class); private final Map<ClassDefinition,RefResolver<AbstractRef, ?, ?>> resolvers; private boolean doSkipNext; // skip the resolving for the next object (required if the outer object is in the map itself) public ReferencingParser(byte[] buffer, int offset, int length, Map<ClassDefinition,RefResolver<AbstractRef, ?, ?>> resolvers, boolean skipFirst) { super(buffer, offset, length); this.resolvers = resolvers; this.doSkipNext = skipFirst; } public void skipNext() { doSkipNext = true; } protected RefResolver<AbstractRef, ?, ?> getReferencedResolver(ObjectReference di) { return di.getLowerBound() == null ? null : resolvers.get(di.getLowerBound()); } @Override public <R extends BonaPortable> R readObject (ObjectReference di, Class<R> type) throws MessageParserException { if (doSkipNext) { doSkipNext = false; return super.readObject(di, type); } final RefResolver<AbstractRef, ?, ?> r = getReferencedResolver(di); if (r == null) return super.readObject(di, type); // read a long and resolve it if (checkForNull(di)) return null; long ref = readLong(needToken(), di.getName()); if (ref <= 0L) return null; // mapping 0 => null try { BonaPortable newObject = r.getDTO(Long.valueOf(ref)); if (newObject.getClass() != type) { // check if it is a superclass if (!di.getAllowSubclasses() || !type.isAssignableFrom(newObject.getClass())) { throw newMPE(MessageParserException.BAD_CLASS, String.format("(got %s, expected %s for %s, subclassing = %b)", newObject.getClass().getSimpleName(), type.getSimpleName(), di.getName(), Boolean.valueOf(di.getAllowSubclasses())) ); } } return type.cast(newObject); } catch (ApplicationException e) { throw newMPE(MessageParserException.INVALID_REFERENCES, e.getMessage()); } } protected int collectionStart(FieldDefinition di, int storedCount) { if (storedCount != COLLECTION_COUNT_REF) { return storedCount; } else { if (di instanceof ObjectReference) { final RefResolver<AbstractRef, ?, ?> r = getReferencedResolver((ObjectReference)di); if (r == null) { LOGGER.warn("Resolver for {} not provided but referenced in stored instance of {}.{}", ((ObjectReference)di).getLowerBound().getName(), currentClass, di.getName()); } } return di.getIsAggregateRequired() ? 0 : COLLECTION_COUNT_NULL; // currently always LAZY } } @Override public int parseMapStart(FieldDefinition di) throws MessageParserException { return collectionStart(di, super.parseMapStart(di)); } @Override public int parseArrayStart(FieldDefinition di, int sizeOfElement) throws MessageParserException { return collectionStart(di, super.parseArrayStart(di, sizeOfElement)); } }