/**
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.waveprotocol.box.server.waveserver;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import org.waveprotocol.wave.federation.Proto.ProtocolAppliedWaveletDelta;
import org.waveprotocol.wave.federation.Proto.ProtocolWaveletDelta;
/**
* Bundles a protocol buffer with its serialised representation.
* <p>
* This class is used to represent objects whose serialised representation
* must be kept intact, because signatures or hashes depend
* on the exact representation as bytes.
* <p>
* This would not be an issue if
* protocol buffer serialisation was unique, but that's not the case.
* We can only count on the equality
*
* {@code message.equals(T.parseFrom(message.toByteString()))},
*
* but not
*
* {@code byteString.equals(T.parseFrom(byteString).toByteString())}.
*
* @param <T> the protocol buffer {@link Message} type.
*/
public final class ByteStringMessage<T extends Message> {
/**
* Parses a {@link ByteStringMessage} from its {@link ByteString}
* representation.
*
* @param prototype used to create a {@link Message.Builder} to parse the
* message
* @param byteString representation of the message
* @throws InvalidProtocolBufferException if {@code byteString} is not a
* valid protocol buffer
*/
public static <K extends Message> ByteStringMessage<K> parseFrom(K prototype,
ByteString byteString) throws InvalidProtocolBufferException {
return new ByteStringMessage<K>(parse(prototype, byteString), byteString);
}
@SuppressWarnings("unchecked")
private static <K extends Message> K parse(K prototype, ByteString byteString)
throws InvalidProtocolBufferException {
return (K) prototype.newBuilderForType().mergeFrom(byteString).build();
}
/**
* Parses a {@link ProtocolWaveletDelta}. Convenience method, equivalent to
* {@code from(ProtocolWaveletDelta.getDefaultInstanceForType(), byteString)}.
*/
public static ByteStringMessage<ProtocolWaveletDelta> parseProtocolWaveletDelta(
ByteString byteString) throws InvalidProtocolBufferException {
return new ByteStringMessage<ProtocolWaveletDelta>(
ProtocolWaveletDelta.parseFrom(byteString), byteString);
}
/**
* Parses a {@link ProtocolAppliedWaveletDelta}. Convenience method, equivalent to
* {@code from(ProtocolAppliedWaveletDelta.getDefaultInstanceForType(), byteString)}.
*/
public static ByteStringMessage<ProtocolAppliedWaveletDelta> parseProtocolAppliedWaveletDelta(
ByteString byteString) throws InvalidProtocolBufferException {
return new ByteStringMessage<ProtocolAppliedWaveletDelta>(
ProtocolAppliedWaveletDelta.parseFrom(byteString), byteString);
}
/**
* Serialises a {@link Message} into a {@link ByteStringMessage}.
* This should only be used once, when the message is originally created.
*
* @param message to form the serialised version of
*/
public static <K extends Message> ByteStringMessage<K> serializeMessage(K message) {
return new ByteStringMessage<K>(message, message.toByteString());
}
private final ByteString byteString;
private final T message;
/**
* Constructs a {@link ByteStringMessage} from corresponding {@link Message}
* and serialised {@link ByteString} representations of protocol buffer.
*
* @param message a protocol message equal to {@code T.parseFrom(byteString)}
* @param byteString representation of {@code message}
*/
private ByteStringMessage(T message, ByteString byteString) {
this.message = message;
this.byteString = byteString;
}
/**
* @return the immutable underlying {@code Message}
*/
public T getMessage() {
return message;
}
/**
* @return the serialised representation of this message
*/
public ByteString getByteString() {
return this.byteString;
}
/**
* @return the serialised byte array representation of this message
*/
public byte[] getByteArray() {
return this.byteString.toByteArray();
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object o) {
if (!(o instanceof ByteStringMessage)) {
return false;
} else {
ByteStringMessage<Message> bsm = (ByteStringMessage<Message>) o;
return byteString.equals(bsm.byteString);
}
}
@Override
public int hashCode() {
return byteString.hashCode();
}
@Override
public String toString() {
return "ByteStringMessage: " + message;
}
}