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 31package com.google.protobuf; 32 33import java.io.IOException; 34import java.util.Iterator; 35import 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 */ 49class 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 59 public LazyField(MessageLite defaultInstance, 60 ExtensionRegistryLite extensionRegistry, ByteString bytes) { 61 this.defaultInstance = defaultInstance; 62 this.extensionRegistry = extensionRegistry; 63 this.bytes = bytes; 64 } 65 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 */ 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 */ 88 public int getSerializedSize() { 89 if (isDirty) { 90 return value.getSerializedSize(); 91 } 92 return bytes.size(); 93 } 94 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 110 public int hashCode() { 111 ensureInitialized(); 112 return value.hashCode(); 113 } 114 115 @Override 116 public boolean equals(Object obj) { 117 ensureInitialized(); 118 return value.equals(obj); 119 } 120 121 @Override 122 public String toString() { 123 ensureInitialized(); 124 return value.toString(); 125 } 126 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 156 private LazyEntry(Entry<K, LazyField> entry) { 157 this.entry = entry; 158 } 159 160 @Override 161 public K getKey() { 162 return entry.getKey(); 163 } 164 165 @Override 166 public Object getValue() { 167 LazyField field = entry.getValue(); 168 if (field == null) { 169 return null; 170 } 171 return field.getValue(); 172 } 173 174 public LazyField getField() { 175 return entry.getValue(); 176 } 177 178 @Override 179 public Object setValue(Object value) { 180 if (!(value instanceof MessageLite)) { 181 throw new IllegalArgumentException( 182 "LazyField now only used for MessageSet, " 183 + "and the value of MessageSet must be an instance of MessageLite"); 184 } 185 return entry.getValue().setValue((MessageLite) value); 186 } 187 } 188 189 static class LazyIterator<K> implements Iterator<Entry<K, Object>> { 190 private Iterator<Entry<K, Object>> iterator; 191 192 public LazyIterator(Iterator<Entry<K, Object>> iterator) { 193 this.iterator = iterator; 194 } 195 196 @Override 197 public boolean hasNext() { 198 return iterator.hasNext(); 199 } 200 201 @SuppressWarnings("unchecked") 202 @Override 203 public Entry<K, Object> next() { 204 Entry<K, ?> entry = iterator.next(); 205 if (entry.getValue() instanceof LazyField) { 206 return new LazyEntry<K>((Entry<K, LazyField>) entry); 207 } 208 return (Entry<K, Object>) entry; 209 } 210 211 @Override 212 public void remove() { 213 iterator.remove(); 214 } 215 } 216} 217