001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.fusesource.hawtbuf.proto; 018 019 import java.io.IOException; 020 import java.io.InputStream; 021 import java.io.OutputStream; 022 import java.util.ArrayList; 023 import java.util.Collection; 024 import java.util.List; 025 026 import org.fusesource.hawtbuf.Buffer; 027 import org.fusesource.hawtbuf.BufferOutputStream; 028 029 030 abstract public class BaseMessage<T> implements Message<T> { 031 032 protected int memoizedSerializedSize = -1; 033 034 abstract public T clone() throws CloneNotSupportedException; 035 036 public void clear() { 037 memoizedSerializedSize = -1; 038 } 039 040 public boolean isInitialized() { 041 return missingFields().isEmpty(); 042 } 043 044 @SuppressWarnings("unchecked") 045 public T assertInitialized() throws UninitializedMessageException { 046 java.util.ArrayList<String> missingFields = missingFields(); 047 if (!missingFields.isEmpty()) { 048 throw new UninitializedMessageException(missingFields); 049 } 050 return getThis(); 051 } 052 053 @SuppressWarnings("unchecked") 054 protected T checktInitialized() throws InvalidProtocolBufferException { 055 java.util.ArrayList<String> missingFields = missingFields(); 056 if (!missingFields.isEmpty()) { 057 throw new UninitializedMessageException(missingFields).asInvalidProtocolBufferException(); 058 } 059 return getThis(); 060 } 061 062 public ArrayList<String> missingFields() { 063 load(); 064 return new ArrayList<String>(); 065 } 066 067 protected void loadAndClear() { 068 memoizedSerializedSize = -1; 069 } 070 071 protected void load() { 072 } 073 074 @SuppressWarnings("unchecked") 075 public T mergeFrom(T other) { 076 return getThis(); 077 } 078 079 public void writeUnframed(CodedOutputStream output) throws java.io.IOException { 080 // if (encodedForm == null) { 081 // encodedForm = new byte[serializedSizeUnframed()]; 082 // com.google.protobuf.CodedOutputStream original = output; 083 // output = 084 // com.google.protobuf.CodedOutputStream.newInstance(encodedForm); 085 // if (hasField1()) { 086 // output.writeInt32(1, getField1()); 087 // } 088 // if (hasField2()) { 089 // output.writeInt64(2, getField2()); 090 // } 091 // if (hasField3()) { 092 // writeMessage(output, 3, getField3()); 093 // } 094 // output.checkNoSpaceLeft(); 095 // output = original; 096 // } 097 // output.writeRawBytes(encodedForm); 098 } 099 100 // ///////////////////////////////////////////////////////////////// 101 // Write related helpers. 102 // ///////////////////////////////////////////////////////////////// 103 104 public void writeFramed(CodedOutputStream output) throws IOException { 105 output.writeRawVarint32(serializedSizeUnframed()); 106 writeUnframed(output); 107 } 108 109 public Buffer toUnframedBuffer() { 110 try { 111 int size = serializedSizeUnframed(); 112 BufferOutputStream baos = new BufferOutputStream(size); 113 CodedOutputStream output = new CodedOutputStream(baos); 114 writeUnframed(output); 115 Buffer rc = baos.toBuffer(); 116 if( rc.length != size ) { 117 throw new IllegalStateException("Did not write as much data as expected."); 118 } 119 return rc; 120 } catch (IOException e) { 121 throw new RuntimeException("Serializing to a byte array threw an IOException " + "(should never happen).", e); 122 } 123 } 124 125 public Buffer toFramedBuffer() { 126 try { 127 int size = serializedSizeFramed(); 128 BufferOutputStream baos = new BufferOutputStream(size); 129 CodedOutputStream output = new CodedOutputStream(baos); 130 writeFramed(output); 131 Buffer rc = baos.toBuffer(); 132 if( rc.length != size ) { 133 throw new IllegalStateException("Did not write as much data as expected."); 134 } 135 return rc; 136 } catch (IOException e) { 137 throw new RuntimeException("Serializing to a byte array threw an IOException " + "(should never happen).", e); 138 } 139 } 140 141 public byte[] toUnframedByteArray() { 142 return toUnframedBuffer().toByteArray(); 143 } 144 145 public byte[] toFramedByteArray() { 146 return toFramedBuffer().toByteArray(); 147 } 148 149 public void writeFramed(OutputStream output) throws IOException { 150 CodedOutputStream codedOutput = new CodedOutputStream(output); 151 writeFramed(codedOutput); 152 codedOutput.flush(); 153 } 154 155 public void writeUnframed(OutputStream output) throws IOException { 156 CodedOutputStream codedOutput = new CodedOutputStream(output); 157 writeUnframed(codedOutput); 158 codedOutput.flush(); 159 } 160 161 public int serializedSizeFramed() { 162 int t = serializedSizeUnframed(); 163 return CodedOutputStream.computeRawVarint32Size(t) + t; 164 165 } 166 167 // ///////////////////////////////////////////////////////////////// 168 // Read related helpers. 169 // ///////////////////////////////////////////////////////////////// 170 171 public T mergeFramed(CodedInputStream input) throws IOException { 172 int length = input.readRawVarint32(); 173 int oldLimit = input.pushLimit(length); 174 T rc = mergeUnframed(input); 175 input.checkLastTagWas(0); 176 input.popLimit(oldLimit); 177 return rc; 178 } 179 180 public T mergeUnframed(Buffer data) throws InvalidProtocolBufferException { 181 try { 182 CodedInputStream input = new CodedInputStream(data); 183 mergeUnframed(input); 184 input.checkLastTagWas(0); 185 return getThis(); 186 } catch (InvalidProtocolBufferException e) { 187 throw e; 188 } catch (IOException e) { 189 throw new RuntimeException("An IOException was thrown (should never happen in this method).", e); 190 } 191 } 192 193 @SuppressWarnings("unchecked") 194 private T getThis() { 195 return (T) this; 196 } 197 198 public T mergeFramed(Buffer data) throws InvalidProtocolBufferException { 199 try { 200 CodedInputStream input = new CodedInputStream(data); 201 mergeFramed(input); 202 input.checkLastTagWas(0); 203 return getThis(); 204 } catch (InvalidProtocolBufferException e) { 205 throw e; 206 } catch (IOException e) { 207 throw new RuntimeException("An IOException was thrown (should never happen in this method).", e); 208 } 209 } 210 211 public T mergeUnframed(byte[] data) throws InvalidProtocolBufferException { 212 return mergeUnframed(new Buffer(data)); 213 } 214 215 public T mergeFramed(byte[] data) throws InvalidProtocolBufferException { 216 return mergeFramed(new Buffer(data)); 217 } 218 219 public T mergeUnframed(InputStream input) throws IOException { 220 CodedInputStream codedInput = new CodedInputStream(input); 221 mergeUnframed(codedInput); 222 return getThis(); 223 } 224 225 public T mergeFramed(InputStream input) throws IOException { 226 int length = readRawVarint32(input); 227 byte[] data = new byte[length]; 228 int pos = 0; 229 while (pos < length) { 230 int r = input.read(data, pos, length - pos); 231 if (r < 0) { 232 throw new InvalidProtocolBufferException("Input stream ended before a full message frame could be read."); 233 } 234 pos += r; 235 } 236 return mergeUnframed(data); 237 } 238 239 // ///////////////////////////////////////////////////////////////// 240 // Internal implementation methods. 241 // ///////////////////////////////////////////////////////////////// 242 static protected <T> void addAll(Iterable<T> values, Collection<? super T> list) { 243 if (values instanceof Collection) { 244 @SuppressWarnings("unsafe") 245 Collection<T> collection = (Collection<T>) values; 246 list.addAll(collection); 247 } else { 248 for (T value : values) { 249 list.add(value); 250 } 251 } 252 } 253 254 static protected void writeGroup(CodedOutputStream output, int tag, BaseMessage message) throws IOException { 255 output.writeTag(tag, WireFormat.WIRETYPE_START_GROUP); 256 message.writeUnframed(output); 257 output.writeTag(tag, WireFormat.WIRETYPE_END_GROUP); 258 } 259 260 static protected <T extends BaseMessage> T readGroup(CodedInputStream input, int tag, T group) throws IOException { 261 group.mergeUnframed(input); 262 input.checkLastTagWas(WireFormat.makeTag(tag, WireFormat.WIRETYPE_END_GROUP)); 263 return group; 264 } 265 266 static protected int computeGroupSize(int tag, BaseMessage message) { 267 return CodedOutputStream.computeTagSize(tag) * 2 + message.serializedSizeUnframed(); 268 } 269 270 static protected void writeMessage(CodedOutputStream output, int tag, BaseMessage message) throws IOException { 271 output.writeTag(tag, WireFormat.WIRETYPE_LENGTH_DELIMITED); 272 message.writeFramed(output); 273 } 274 275 static protected int computeMessageSize(int tag, BaseMessage message) { 276 return CodedOutputStream.computeTagSize(tag) + message.serializedSizeFramed(); 277 } 278 279 protected List<String> prefix(List<String> missingFields, String prefix) { 280 ArrayList<String> rc = new ArrayList<String>(missingFields.size()); 281 for (String v : missingFields) { 282 rc.add(prefix + v); 283 } 284 return rc; 285 } 286 287 /** 288 * Read a raw Varint from the stream. If larger than 32 bits, discard the 289 * upper bits. 290 */ 291 static public int readRawVarint32(InputStream is) throws IOException { 292 byte tmp = readRawByte(is); 293 if (tmp >= 0) { 294 return tmp; 295 } 296 int result = tmp & 0x7f; 297 if ((tmp = readRawByte(is)) >= 0) { 298 result |= tmp << 7; 299 } else { 300 result |= (tmp & 0x7f) << 7; 301 if ((tmp = readRawByte(is)) >= 0) { 302 result |= tmp << 14; 303 } else { 304 result |= (tmp & 0x7f) << 14; 305 if ((tmp = readRawByte(is)) >= 0) { 306 result |= tmp << 21; 307 } else { 308 result |= (tmp & 0x7f) << 21; 309 result |= (tmp = readRawByte(is)) << 28; 310 if (tmp < 0) { 311 // Discard upper 32 bits. 312 for (int i = 0; i < 5; i++) { 313 if (readRawByte(is) >= 0) 314 return result; 315 } 316 throw new InvalidProtocolBufferException("CodedInputStream encountered a malformed varint."); 317 } 318 } 319 } 320 } 321 return result; 322 } 323 324 static protected byte readRawByte(InputStream is) throws IOException { 325 int rc = is.read(); 326 if (rc == -1) { 327 throw new InvalidProtocolBufferException("While parsing a protocol message, the input ended unexpectedly " + "in the middle of a field. This could mean either than the " + "input has been truncated or that an embedded message " 328 + "misreported its own length."); 329 } 330 return (byte) rc; 331 } 332 333 }