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