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 }