| /* |
| * Copyright 2011 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package com.google.web.bindery.autobean.shared.impl; |
| |
| import com.google.web.bindery.autobean.shared.Splittable; |
| import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.Coder; |
| import com.google.web.bindery.autobean.shared.impl.AutoBeanCodexImpl.EncodeState; |
| |
| import java.util.AbstractList; |
| |
| /** |
| * A list implementation that lazily reifies its constituent elements. |
| * |
| * @param <E> the element type |
| */ |
| public class SplittableList<E> extends AbstractList<E> implements HasSplittable { |
| static <Q> Q reify(EncodeState state, Splittable data, int index, Coder coder) { |
| if (data.isNull(index)) { |
| return null; |
| } |
| @SuppressWarnings("unchecked") |
| Q toReturn = (Q) coder.decode(state, data.get(index)); |
| data.setReified(String.valueOf(index), toReturn); |
| return toReturn; |
| } |
| |
| static void set(EncodeState state, Splittable data, int index, Coder coder, Object value) { |
| data.setReified(String.valueOf(index), value); |
| if (value == null) { |
| Splittable.NULL.assign(data, index); |
| return; |
| } |
| Splittable backing = coder.extractSplittable(state, value); |
| if (backing == null) { |
| /* |
| * External data type, such as an ArrayList or a concrete implementation |
| * of a setter's interface type. This means that a slow serialization pass |
| * is necessary. |
| */ |
| data.setReified(AbstractAutoBean.UNSPLITTABLE_VALUES_KEY, true); |
| } else { |
| backing.assign(data, index); |
| } |
| } |
| |
| private Splittable data; |
| private final Coder elementCoder; |
| private final EncodeState state; |
| |
| public SplittableList(Splittable data, Coder elementCoder, EncodeState state) { |
| assert data.isIndexed() : "Expecting indexed data"; |
| this.data = data; |
| this.elementCoder = elementCoder; |
| this.state = state; |
| } |
| |
| @Override |
| public void add(int index, E element) { |
| set(state, data, index, elementCoder, element); |
| } |
| |
| @Override |
| public E get(int index) { |
| if (data.isReified(String.valueOf(index))) { |
| @SuppressWarnings("unchecked") |
| E toReturn = (E) data.getReified(String.valueOf(index)); |
| return toReturn; |
| } |
| // javac generics bug |
| return SplittableList.<E> reify(state, data, index, elementCoder); |
| } |
| |
| public Splittable getSplittable() { |
| return data; |
| } |
| |
| @Override |
| public E remove(int index) { |
| E toReturn = get(index); |
| // XXX This is terrible, use Array.splice |
| int newSize = data.size() - 1; |
| for (int i = index; i < newSize; i++) { |
| data.get(i + 1).assign(data, i); |
| // reified value for index 'i' may needs to be updated |
| if (data.isReified(String.valueOf(i + 1))) { |
| data.setReified(String.valueOf(i), data.getReified(String.valueOf(i + 1))); |
| } else if (data.isReified(String.valueOf(i))) { |
| // Remove outdated value. Will be regenerated if needed. |
| data.removeReified(String.valueOf(i)); |
| } |
| } |
| // clean-up last entry as one element has been removed |
| data.removeReified(String.valueOf(newSize)); |
| data.setSize(newSize); |
| return toReturn; |
| } |
| |
| @Override |
| public E set(int index, E element) { |
| E previous = get(index); |
| set(state, data, index, elementCoder, element); |
| return previous; |
| } |
| |
| @Override |
| public int size() { |
| return data.size(); |
| } |
| } |