blob: a23ee726900f30ec06bf32d984dad5a943817269 [file] [log] [blame]
/*
* Copyright 2009 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.gwt.collections;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
/**
* An array whose content and length can change over time. This implementation
* is used in web mode.
*
* @param <E> The type stored in the array elements
*/
public class MutableArray<E> extends Array<E> {
JsArray elems;
private boolean frozen;
/**
* Can only be constructed via {@link CollectionFactory}.
*/
MutableArray() {
elems = (JsArray) JavaScriptObject.createArray();
}
@ConstantTime
public void add(E elem) {
Assertions.assertNotFrozen(this);
jsniAdd(elem);
}
@ConstantTime
public void clear() {
Assertions.assertNotFrozen(this);
elems.setLength(0);
}
/**
* Creates an immutable array based on this one. Also marks this object as
* read-only. After calling {@code freeze()}, only use methods from
* {@link Array} or the returned {@link ImmutableArray} should be to access
* the elements of the array is preferred.
*/
public ImmutableArray<E> freeze() {
Assertions.markFrozen(this);
if (size() != 0) {
return new ImmutableArrayImpl(elems);
} else {
return ImmutableArray.getEmptyInstance();
}
}
@Override
@ConstantTime
public E get(int index) {
Assertions.assertIndexInRange(index, 0, size());
return jsniGet(index);
}
/**
* Inserts {@code element} before the element residing at {@code index}.
*
* @param index in the range [0, this.size()], inclusive; if index is equal to
* the array's current size, the result is equivalent to calling
* {@link #add(Object)}
* @param elem the element to insert or {@code null}
*/
@LinearTime
public void insert(int index, E elem) {
Assertions.assertNotFrozen(this);
Assertions.assertIndexInRange(index, 0, size() + 1);
jsniInsert(index, elem);
}
/**
* Removes the element at the specified index.
*/
@LinearTime
public void remove(int index) {
Assertions.assertNotFrozen(this);
Assertions.assertIndexInRange(index, 0, size());
jsniRemove(index);
}
/**
* Replaces the element at the specified index.
*
* @param index in the range [0, this.size()), exclusive
* @param elem the element to insert or {@code null}
*/
@ConstantTime
public void set(int index, E elem) {
Assertions.assertNotFrozen(this);
Assertions.assertIndexInRange(index, 0, size());
jsniSet(index, elem);
}
/**
* Changes the array size. If {@code newSize} is less than the current size,
* the array is truncated. If {@code newSize} is greater than the current size
* the array is grown and the new elements of the array filled up with {@code
* fillValue}.
*/
@LinearTime
public void setSize(int newSize, E fillValue) {
Assertions.assertNotFrozen(this);
jsniSetSize(newSize, fillValue);
}
@Override
@ConstantTime
public int size() {
return elems.length();
}
// Only meant to be called from within Assertions
boolean isFrozen() {
return frozen;
}
// Only meant to be called from within Assertions
void markFrozen() {
frozen = true;
}
private Object[] copyNativeArray(JsArray jsElems) {
if (jsElems == null) {
return null;
}
Object[] jreElems = new Object[jsElems.length()];
for (int i = 0; i < jsElems.length(); ++i) {
jreElems[i] = jsElems.get(i);
}
return jreElems;
}
@ConstantTime
private native void jsniAdd(E elem) /*-{
this.@com.google.gwt.collections.MutableArray::elems.push(elem);
}-*/;
@ConstantTime
private native E jsniGet(int index) /*-{
return this.@com.google.gwt.collections.MutableArray::elems[index];
}-*/;
/**
* Inserts {@code element} before the element residing at {@code index}.
*
* @param index in the range [0, this.size()], inclusive; if index is equal to
* the array's current size, the result is equivalent to calling
* {@link #add(Object)}
* @param elem the element to insert or {@code null}
*/
@LinearTime
private native void jsniInsert(int index, E elem) /*-{
this.@com.google.gwt.collections.MutableArray::elems.splice(index, 0, elem);
}-*/;
/**
* Removes the element at the specified index.
*/
@LinearTime
private native void jsniRemove(int index) /*-{
this.@com.google.gwt.collections.MutableArray::elems.splice(index, 1);
}-*/;
/**
* Replaces the element at the specified index.
*
* @param index in the range [0, this.size()), exclusive
* @param elem the element to insert or {@code null}
*/
@ConstantTime
private native void jsniSet(int index, E elem) /*-{
this.@com.google.gwt.collections.MutableArray::elems[index] = elem;
}-*/;
/**
* Changes the array size. If {@code newSize} is less than the current size,
* the array is truncated. If {@code newSize} is greater than the current size
* the array is grown and the new elements of the array filled up with {@code
* fillValue}.
*/
@LinearTime
private native void jsniSetSize(int newSize, E fillValue) /*-{
var fillStart;
var i;
fillStart = this.@com.google.gwt.collections.MutableArray::elems.length;
this.@com.google.gwt.collections.MutableArray::elems.length = newSize;
if (fillValue != null) {
for (i = fillStart; i < newSize; ++i) {
this.@com.google.gwt.collections.MutableArray::elems[i] = fillValue;
}
}
}-*/;
}