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    }