package xdi2.messaging; import java.io.Serializable; import java.util.Iterator; import xdi2.core.ContextNode; import xdi2.core.Graph; import xdi2.core.constants.XDIConstants; import xdi2.core.features.nodetypes.XdiEntityCollection; import xdi2.core.features.nodetypes.XdiEntityCollection.MappingContextNodeXdiEntityCollectionIterator; import xdi2.core.features.nodetypes.XdiInnerRoot; import xdi2.core.impl.memory.MemoryGraphFactory; import xdi2.core.syntax.XDIAddress; import xdi2.core.syntax.XDIStatement; import xdi2.core.util.iterators.DescendingIterator; import xdi2.core.util.iterators.EmptyIterator; import xdi2.core.util.iterators.IteratorCounter; import xdi2.core.util.iterators.IteratorListMaker; import xdi2.core.util.iterators.MappingIterator; import xdi2.core.util.iterators.NotNullIterator; import xdi2.core.util.iterators.ReadOnlyIterator; import xdi2.core.util.iterators.SingleItemIterator; import xdi2.messaging.constants.XDIMessagingConstants; import xdi2.messaging.operations.Operation; /** * An XDI message envelope, represented as a graph. * * @author markus */ public class MessageEnvelope implements Serializable, Comparable<MessageEnvelope> { private static final long serialVersionUID = -7335038610687761197L; protected static final MemoryGraphFactory graphFactory = MemoryGraphFactory.getInstance(); protected Graph graph; protected MessageEnvelope(Graph graph) { this.graph = graph; } public MessageEnvelope() { this(graphFactory.openGraph()); } /* * Static methods */ /** * Checks if a graph is a valid XDI message envelope. * @param graph The graph to check. * @return True if the graph is a valid XDI message envelope. */ public static boolean isValid(Graph graph) { return true; } /** * Factory method that creates an XDI message envelope bound to a given graph. * @param graph The graph that is an XDI message envelope. * @return The XDI message envelope. */ public static MessageEnvelope fromGraph(Graph graph) { if (! isValid(graph)) return null; return new MessageEnvelope(graph); } /** * Factory method that creates an XDI message envelope bound to a given graph. * @param operationXDIAddress The operation identifier to use for the new operation. * @param targetXDIAddress The target address to which the operation applies. * @return The XDI message envelope. */ public static MessageEnvelope fromOperationXDIAddressAndTargetXDIAddress(XDIAddress operationXDIAddress, XDIAddress targetXDIAddress) { if (targetXDIAddress == null) targetXDIAddress = XDIConstants.XDI_ADD_ROOT; MessageEnvelope messageEnvelope = new MessageEnvelope(); Message message = messageEnvelope.createMessage(XDIMessagingConstants.XDI_ADD_ANONYMOUS); message.createOperation(operationXDIAddress, targetXDIAddress); return messageEnvelope; } /** * Factory method that creates an XDI message envelope bound to a given graph. * @param operationXDIAddress The operation identifier to use for the new operation. * @param targetXDIStatements The target statements to which the operation applies. * @return The XDI message envelope. */ public static MessageEnvelope fromOperationXDIAddressAndTargetXDIStatements(XDIAddress operationXDIAddress, Iterator<XDIStatement> targetXDIStatements) { if (targetXDIStatements == null) throw new NullPointerException(); MessageEnvelope messageEnvelope = new MessageEnvelope(); Message message = messageEnvelope.createMessage(XDIMessagingConstants.XDI_ADD_ANONYMOUS); message.createOperation(operationXDIAddress, targetXDIStatements); return messageEnvelope; } /** * Factory method that creates an XDI message envelope bound to a given graph. * @param operationXDIAddress The operation identifier to use for the new operation. * @param targetXDIAddressOrTargetStatement The target address or target statement to which the operation applies. * @return The XDI message envelope. */ public static final MessageEnvelope fromOperationXDIAddressAndTargetXDIAddressOrTargetXDIStatement(XDIAddress operationXDIAddress, String targetXDIAddressOrTargetStatement) { try { if (targetXDIAddressOrTargetStatement == null) targetXDIAddressOrTargetStatement = ""; XDIAddress targetXDIAddress = XDIAddress.create(targetXDIAddressOrTargetStatement); return MessageEnvelope.fromOperationXDIAddressAndTargetXDIAddress(operationXDIAddress, targetXDIAddress); } catch (Exception ex) { XDIStatement targetXDIStatement = XDIStatement.create(targetXDIAddressOrTargetStatement); return MessageEnvelope.fromOperationXDIAddressAndTargetXDIStatements(operationXDIAddress, new SingleItemIterator<XDIStatement> (targetXDIStatement)); } } /* * Instance methods */ /** * Returns the underlying graph to which this XDI message envelope is bound. * @return A graph that represents the XDI message envelope. */ public Graph getGraph() { return this.graph; } /** * Returns an existing XDI message collection in this XDI message envelope, or creates a new one. * @param senderXDIAddress The sender. * @param create Whether to create an XDI message collection if it does not exist. * @return The existing or newly created XDI message collection. */ public MessageCollection getMessageCollection(XDIAddress senderXDIAddress, boolean create) { if (senderXDIAddress == null) senderXDIAddress = XDIMessagingConstants.XDI_ADD_ANONYMOUS; XDIAddress messageCollectionXDIAddress = XDIAddress.create(senderXDIAddress.toString() + XdiEntityCollection.createXDIArc(XDIMessagingConstants.XDI_ARC_MSG)); ContextNode contextNode = create ? this.getGraph().setDeepContextNode(messageCollectionXDIAddress) : this.getGraph().getDeepContextNode(messageCollectionXDIAddress, true); if (contextNode == null) return null; XdiEntityCollection xdiEntityCollection = XdiEntityCollection.fromContextNode(contextNode); return new MessageCollection(this, xdiEntityCollection); } /** * Returns an existing XDI message collection in this XDI message envelope, or creates a new one. * @param create Whether to create an XDI message collection if it does not exist. * @return The existing or newly created XDI message collection. */ public MessageCollection getMessageCollection(boolean create) { return this.getMessageCollection(null, create); } /** * Returns all message collections in this message envelope. * @return All message collections in the envelope. */ public ReadOnlyIterator<MessageCollection> getMessageCollections() { // get all context nodes that are valid XDI message collections Iterator<ContextNode> contextNodes = this.getGraph().getRootContextNode(true).getAllContextNodes(); return new MappingXdiEntityCollectionMessageCollectionIterator( this, new MappingContextNodeXdiEntityCollectionIterator( contextNodes)); } /** * Deletes all message collections from this message envelope. */ public void deleteMessageCollections() { for (MessageCollection messageCollection : new IteratorListMaker<MessageCollection> (this.getMessageCollections()).list()) { messageCollection.getContextNode().delete(); } } /** * Returns all messages in this message envelope. * @return All messages contained in the envelope. */ public ReadOnlyIterator<Message> getMessages() { return new DescendingIterator<MessageCollection, Message> (this.getMessageCollections()) { @Override public Iterator<Message> descend(MessageCollection messageCollection) { return messageCollection.getMessages(); } }; } /** * Finds messages with a given sender in this message envelope. * @param senderXDIAddress The sender to look for. * @return The messages. */ public ReadOnlyIterator<Message> getMessages(XDIAddress senderXDIAddress) { MessageCollection messageCollection = this.getMessageCollection(senderXDIAddress, false); if (messageCollection == null) return new EmptyIterator<Message> (); return messageCollection.getMessages(); } /** * Finds a message by its XDI address. * @param messageXDIAddress The message to look for. * @return The message. */ public Message getMessage(XDIAddress messageXDIAddress) { if (messageXDIAddress == null) throw new NullPointerException(); for (Message message : this.getMessages()) { if (messageXDIAddress.equals(message.getContextNode().getXDIAddress())) return message; } return null; } /** * Returns all operations in this message envelope. * @return All operations contained in the envelope. */ public ReadOnlyIterator<Operation> getOperations() { return new DescendingIterator<Message, Operation> (this.getMessages()) { @Override public Iterator<Operation> descend(Message message) { return message.getOperations(); } }; } /** * Returns all operation results in this message envelope. * @return All operation results contained in the envelope. */ public ReadOnlyIterator<XdiInnerRoot> getOperationResults() { return new DescendingIterator<Message, XdiInnerRoot> (this.getMessages()) { @Override public Iterator<XdiInnerRoot> descend(Message message) { return message.getOperationResults(); } }; } /** * Returns the number of message collections in the message envelope. */ public long getMessageCollectionCount() { Iterator<MessageCollection> iterator = this.getMessageCollections(); return new IteratorCounter(iterator).count(); } /** * Returns the number of messages in the message envelope. */ public long getMessageCount() { Iterator<Message> iterator = this.getMessages(); return new IteratorCounter(iterator).count(); } /** * Returns the number of operations in all messages of the message envelope. */ public long getOperationCount() { Iterator<Operation> iterator = this.getOperations(); return new IteratorCounter(iterator).count(); } /* * Convenience instance methods for messages */ /** * Creates a new XDI message in this XDI message envelope for a given sender. * @param senderXDIAddress The sender. * @param index Index in an ordered collection. * @return The newly created XDI message. */ public Message createMessage(XDIAddress senderXDIAddress, long index) { return this.getMessageCollection(senderXDIAddress, true).createMessage(index); } /** * Creates a new XDI message in this XDI message envelope for a given sender. * @param senderXDIAddress The sender. * @return The newly created XDI message. */ public Message createMessage(XDIAddress senderXDIAddress) { return this.getMessageCollection(senderXDIAddress, true).createMessage(); } /** * Creates a new XDI message in this XDI message envelope. * @return The newly created XDI message. */ public Message createMessage(long index) { return this.getMessageCollection(true).createMessage(index); } /** * Creates a new XDI message in this XDI message envelope. * @return The newly created XDI message. */ public Message createMessage() { return this.getMessageCollection(true).createMessage(); } /* * Object methods */ @Override public String toString() { return this.getGraph().toString(); } @Override public boolean equals(Object object) { if (object == null || ! (object instanceof MessageEnvelope)) return false; if (object == this) return true; MessageEnvelope other = (MessageEnvelope) object; return this.getGraph().equals(other.getGraph()); } @Override public int hashCode() { int hashCode = 1; hashCode = (hashCode * 31) + this.getGraph().hashCode(); return hashCode; } @Override public int compareTo(MessageEnvelope other) { if (other == this || other == null) return 0; return this.getGraph().compareTo(other.getGraph()); } /* * Helper classes */ public static class MappingXdiEntityCollectionMessageCollectionIterator extends NotNullIterator<MessageCollection> { public MappingXdiEntityCollectionMessageCollectionIterator(final MessageEnvelope messageEnvelope, Iterator<XdiEntityCollection> xdiEntityCollections) { super(new MappingIterator<XdiEntityCollection, MessageCollection> (xdiEntityCollections) { @Override public MessageCollection map(XdiEntityCollection xdiEntityCollection) { return MessageCollection.fromMessageEnvelopeAndXdiEntityCollection(messageEnvelope, xdiEntityCollection); } }); } } }