1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 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 com.google.protobuf.Internal.ProtobufList; 34 import java.util.ArrayList; 35 import java.util.Collections; 36 import java.util.List; 37 38 /** 39 * Utility class that aids in properly manipulating list fields for either the lite or full runtime. 40 */ 41 abstract class ListFieldSchema { 42 // Disallow construction. ListFieldSchema()43 private ListFieldSchema() {} 44 45 private static final ListFieldSchema FULL_INSTANCE = new ListFieldSchemaFull(); 46 private static final ListFieldSchema LITE_INSTANCE = new ListFieldSchemaLite(); 47 mutableListAt(Object msg, long offset)48 abstract <L> List<L> mutableListAt(Object msg, long offset); 49 makeImmutableListAt(Object msg, long offset)50 abstract void makeImmutableListAt(Object msg, long offset); 51 mergeListsAt(Object msg, Object otherMsg, long offset)52 abstract <L> void mergeListsAt(Object msg, Object otherMsg, long offset); 53 full()54 static ListFieldSchema full() { 55 return FULL_INSTANCE; 56 } 57 lite()58 static ListFieldSchema lite() { 59 return LITE_INSTANCE; 60 } 61 62 /** Implementation for the full runtime. */ 63 private static final class ListFieldSchemaFull extends ListFieldSchema { 64 65 private static final Class<?> UNMODIFIABLE_LIST_CLASS = 66 Collections.unmodifiableList(Collections.emptyList()).getClass(); 67 68 @Override mutableListAt(Object message, long offset)69 <L> List<L> mutableListAt(Object message, long offset) { 70 return mutableListAt(message, offset, AbstractProtobufList.DEFAULT_CAPACITY); 71 } 72 73 @Override makeImmutableListAt(Object message, long offset)74 void makeImmutableListAt(Object message, long offset) { 75 List<?> list = (List<?>) UnsafeUtil.getObject(message, offset); 76 Object immutable = null; 77 if (list instanceof LazyStringList) { 78 immutable = ((LazyStringList) list).getUnmodifiableView(); 79 } else if (UNMODIFIABLE_LIST_CLASS.isAssignableFrom(list.getClass())) { 80 // already immutable 81 return; 82 } else if (list instanceof PrimitiveNonBoxingCollection && list instanceof ProtobufList) { 83 if (((ProtobufList<?>) list).isModifiable()) { 84 ((ProtobufList<?>) list).makeImmutable(); 85 } 86 return; 87 } else { 88 immutable = Collections.unmodifiableList((List<?>) list); 89 } 90 UnsafeUtil.putObject(message, offset, immutable); 91 } 92 93 @SuppressWarnings("unchecked") mutableListAt(Object message, long offset, int additionalCapacity)94 private static <L> List<L> mutableListAt(Object message, long offset, int additionalCapacity) { 95 List<L> list = getList(message, offset); 96 if (list.isEmpty()) { 97 if (list instanceof LazyStringList) { 98 list = (List<L>) new LazyStringArrayList(additionalCapacity); 99 } else if (list instanceof PrimitiveNonBoxingCollection && list instanceof ProtobufList) { 100 list = ((ProtobufList<L>) list).mutableCopyWithCapacity(additionalCapacity); 101 } else { 102 list = new ArrayList<L>(additionalCapacity); 103 } 104 UnsafeUtil.putObject(message, offset, list); 105 } else if (UNMODIFIABLE_LIST_CLASS.isAssignableFrom(list.getClass())) { 106 ArrayList<L> newList = new ArrayList<L>(list.size() + additionalCapacity); 107 newList.addAll(list); 108 list = newList; 109 UnsafeUtil.putObject(message, offset, list); 110 } else if (list instanceof UnmodifiableLazyStringList) { 111 LazyStringArrayList newList = new LazyStringArrayList(list.size() + additionalCapacity); 112 newList.addAll((UnmodifiableLazyStringList) list); 113 list = (List<L>) newList; 114 UnsafeUtil.putObject(message, offset, list); 115 } else if (list instanceof PrimitiveNonBoxingCollection 116 && list instanceof ProtobufList 117 && !((ProtobufList<L>) list).isModifiable()) { 118 list = ((ProtobufList<L>) list).mutableCopyWithCapacity(list.size() + additionalCapacity); 119 UnsafeUtil.putObject(message, offset, list); 120 } 121 return list; 122 } 123 124 @Override mergeListsAt(Object msg, Object otherMsg, long offset)125 <E> void mergeListsAt(Object msg, Object otherMsg, long offset) { 126 List<E> other = getList(otherMsg, offset); 127 List<E> mine = mutableListAt(msg, offset, other.size()); 128 129 int size = mine.size(); 130 int otherSize = other.size(); 131 if (size > 0 && otherSize > 0) { 132 mine.addAll(other); 133 } 134 135 List<E> merged = size > 0 ? mine : other; 136 UnsafeUtil.putObject(msg, offset, merged); 137 } 138 139 @SuppressWarnings("unchecked") getList(Object message, long offset)140 static <E> List<E> getList(Object message, long offset) { 141 return (List<E>) UnsafeUtil.getObject(message, offset); 142 } 143 } 144 145 /** Implementation for the lite runtime. */ 146 private static final class ListFieldSchemaLite extends ListFieldSchema { 147 148 @Override mutableListAt(Object message, long offset)149 <L> List<L> mutableListAt(Object message, long offset) { 150 ProtobufList<L> list = getProtobufList(message, offset); 151 if (!list.isModifiable()) { 152 int size = list.size(); 153 list = 154 list.mutableCopyWithCapacity( 155 size == 0 ? AbstractProtobufList.DEFAULT_CAPACITY : size * 2); 156 UnsafeUtil.putObject(message, offset, list); 157 } 158 return list; 159 } 160 161 @Override makeImmutableListAt(Object message, long offset)162 void makeImmutableListAt(Object message, long offset) { 163 ProtobufList<?> list = getProtobufList(message, offset); 164 list.makeImmutable(); 165 } 166 167 @Override mergeListsAt(Object msg, Object otherMsg, long offset)168 <E> void mergeListsAt(Object msg, Object otherMsg, long offset) { 169 ProtobufList<E> mine = getProtobufList(msg, offset); 170 ProtobufList<E> other = getProtobufList(otherMsg, offset); 171 172 int size = mine.size(); 173 int otherSize = other.size(); 174 if (size > 0 && otherSize > 0) { 175 if (!mine.isModifiable()) { 176 mine = mine.mutableCopyWithCapacity(size + otherSize); 177 } 178 mine.addAll(other); 179 } 180 181 ProtobufList<E> merged = size > 0 ? mine : other; 182 UnsafeUtil.putObject(msg, offset, merged); 183 } 184 185 @SuppressWarnings("unchecked") getProtobufList(Object message, long offset)186 static <E> ProtobufList<E> getProtobufList(Object message, long offset) { 187 return (ProtobufList<E>) UnsafeUtil.getObject(message, offset); 188 } 189 } 190 } 191