• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // http://code.google.com/p/protobuf/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 package com.google.protobuf;
32 
33 import java.io.IOException;
34 import java.util.Iterator;
35 import java.util.Map.Entry;
36 
37 /**
38  * LazyField encapsulates the logic of lazily parsing message fields. It stores
39  * the message in a ByteString initially and then parse it on-demand.
40  *
41  * LazyField is thread-compatible e.g. concurrent read are safe, however,
42  * synchronizations are needed under read/write situations.
43  *
44  * Now LazyField is only used to lazily load MessageSet.
45  * TODO(xiangl): Use LazyField to lazily load all messages.
46  *
47  * @author xiangl@google.com (Xiang Li)
48  */
49 class LazyField {
50 
51   final private MessageLite defaultInstance;
52   final private ExtensionRegistryLite extensionRegistry;
53 
54   // Mutable because it is initialized lazily.
55   private ByteString bytes;
56   private volatile MessageLite value;
57   private volatile boolean isDirty = false;
58 
LazyField(MessageLite defaultInstance, ExtensionRegistryLite extensionRegistry, ByteString bytes)59   public LazyField(MessageLite defaultInstance,
60       ExtensionRegistryLite extensionRegistry, ByteString bytes) {
61     this.defaultInstance = defaultInstance;
62     this.extensionRegistry = extensionRegistry;
63     this.bytes = bytes;
64   }
65 
getValue()66   public MessageLite getValue() {
67     ensureInitialized();
68     return value;
69   }
70 
71   /**
72    * LazyField is not thread-safe for write access. Synchronizations are needed
73    * under read/write situations.
74    */
setValue(MessageLite value)75   public MessageLite setValue(MessageLite value) {
76     MessageLite originalValue = this.value;
77     this.value = value;
78     bytes = null;
79     isDirty = true;
80     return originalValue;
81   }
82 
83   /**
84    * Due to the optional field can be duplicated at the end of serialized
85    * bytes, which will make the serialized size changed after LazyField
86    * parsed. Be careful when using this method.
87    */
getSerializedSize()88   public int getSerializedSize() {
89     if (isDirty) {
90       return value.getSerializedSize();
91     }
92     return bytes.size();
93   }
94 
toByteString()95   public ByteString toByteString() {
96     if (!isDirty) {
97       return bytes;
98     }
99     synchronized (this) {
100       if (!isDirty) {
101         return bytes;
102       }
103       bytes = value.toByteString();
104       isDirty = false;
105       return bytes;
106     }
107   }
108 
109   @Override
hashCode()110   public int hashCode() {
111     ensureInitialized();
112     return value.hashCode();
113   }
114 
115   @Override
equals(Object obj)116   public boolean equals(Object obj) {
117     ensureInitialized();
118     return value.equals(obj);
119   }
120 
121   @Override
toString()122   public String toString() {
123     ensureInitialized();
124     return value.toString();
125   }
126 
ensureInitialized()127   private void ensureInitialized() {
128     if (value != null) {
129       return;
130     }
131     synchronized (this) {
132       if (value != null) {
133         return;
134       }
135       try {
136         if (bytes != null) {
137           value = defaultInstance.getParserForType()
138               .parseFrom(bytes, extensionRegistry);
139         }
140       } catch (IOException e) {
141         // TODO(xiangl): Refactory the API to support the exception thrown from
142         // lazily load messages.
143       }
144     }
145   }
146 
147   // ====================================================
148 
149   /**
150    * LazyEntry and LazyIterator are used to encapsulate the LazyField, when
151    * users iterate all fields from FieldSet.
152    */
153   static class LazyEntry<K> implements Entry<K, Object> {
154     private Entry<K, LazyField> entry;
155 
LazyEntry(Entry<K, LazyField> entry)156     private LazyEntry(Entry<K, LazyField> entry) {
157       this.entry = entry;
158     }
159 
getKey()160     public K getKey() {
161       return entry.getKey();
162     }
163 
getValue()164     public Object getValue() {
165       LazyField field = entry.getValue();
166       if (field == null) {
167         return null;
168       }
169       return field.getValue();
170     }
171 
getField()172     public LazyField getField() {
173       return entry.getValue();
174     }
175 
setValue(Object value)176     public Object setValue(Object value) {
177       if (!(value instanceof MessageLite)) {
178         throw new IllegalArgumentException(
179             "LazyField now only used for MessageSet, "
180             + "and the value of MessageSet must be an instance of MessageLite");
181       }
182       return entry.getValue().setValue((MessageLite) value);
183     }
184   }
185 
186   static class LazyIterator<K> implements Iterator<Entry<K, Object>> {
187     private Iterator<Entry<K, Object>> iterator;
188 
LazyIterator(Iterator<Entry<K, Object>> iterator)189     public LazyIterator(Iterator<Entry<K, Object>> iterator) {
190       this.iterator = iterator;
191     }
192 
hasNext()193     public boolean hasNext() {
194       return iterator.hasNext();
195     }
196 
197     @SuppressWarnings("unchecked")
next()198     public Entry<K, Object> next() {
199       Entry<K, ?> entry = iterator.next();
200       if (entry.getValue() instanceof LazyField) {
201         return new LazyEntry<K>((Entry<K, LazyField>) entry);
202       }
203       return (Entry<K, Object>) entry;
204     }
205 
remove()206     public void remove() {
207       iterator.remove();
208     }
209   }
210 }
211