All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.htmlunit.corejs.javascript.NativeJavaList Maven / Gradle / Ivy

/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.htmlunit.corejs.javascript;

import java.util.ArrayList;
import java.util.List;

/**
 * NativeJavaList is a wrapper for java objects implementing java.util.List
 *  interface. This wrapper delegates index based access in javascript (like 
 * value[x] = 3) to the according {@link List#get(int)}, {@link List#set(int, Object)} and
 * {@link List#add(Object)} methods. This allows you to use java lists in many places like a
 * javascript Array.
 *
 * 

Supported functions: * *

    *
  • index based access is delegated to List.get/set/add. If index >= length, * the skipped elements will be filled with null values *
  • iterator support with for...of (provided by NativeJavaObject for all * iterables) *
  • when iterating with for .. in (or for each .. in) then * getIds * + index based access is used. *
  • reading and setting length property. When modifying the length property, the * list is either truncated or will be filled with null values up to length * *
  • deleting entries: delete value[index] will be equivalent with * value[index] = null and is implemented to provide array compatibility. *
* * Important: JavaList does not support sparse arrays. So setting the length property to a * high value or writing to a high index may allocate a lot of memory. * *

Note: Although JavaList looks like a javascript-Array, it is * not an * Array. Some methods behave very similar like Array.indexOf and * java.util.List.indexOf, others are named differently like Array.includes vs. * java.util.List.contains. Especially forEach is different in Array * and java.util.List. Also deleting entries will set entries to null * instead to Undefined */ public class NativeJavaList extends NativeJavaObject { private static final long serialVersionUID = 660285467829047519L; private List list; @SuppressWarnings("unchecked") public NativeJavaList(Scriptable scope, Object list) { super(scope, list, list.getClass()); assert list instanceof List; this.list = (List) list; } @Override public String getClassName() { return "JavaList"; } @Override public boolean has(String name, Scriptable start) { if (name.equals("length")) { return true; } return super.has(name, start); } @Override public boolean has(int index, Scriptable start) { if (isWithValidIndex(index)) { return true; } return super.has(index, start); } @Override public void delete(int index) { if (isWithValidIndex(index)) { list.set(index, null); } } @Override public boolean has(Symbol key, Scriptable start) { if (SymbolKey.IS_CONCAT_SPREADABLE.equals(key)) { return true; } return super.has(key, start); } @Override public Object get(String name, Scriptable start) { if ("length".equals(name)) { return Integer.valueOf(list.size()); } return super.get(name, start); } @Override public Object get(int index, Scriptable start) { if (isWithValidIndex(index)) { Context cx = Context.getCurrentContext(); Object obj = list.get(index); if (cx != null) { return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass()); } return obj; } return Undefined.instance; } @Override public Object get(Symbol key, Scriptable start) { if (SymbolKey.IS_CONCAT_SPREADABLE.equals(key)) { return Boolean.TRUE; } return super.get(key, start); } @Override public void put(int index, Scriptable start, Object value) { if (index >= 0) { Object javaValue = Context.jsToJava(value, Object.class); if (index == list.size()) { list.add(javaValue); // use "add" at the end of list. } else { ensureCapacity(index + 1); list.set(index, javaValue); } return; } super.put(index, start, value); } @Override public void put(String name, Scriptable start, Object value) { if (list != null && "length".equals(name)) { setLength(value); return; } super.put(name, start, value); } private void ensureCapacity(int minCapacity) { if (minCapacity > list.size()) { if (list instanceof ArrayList) { ((ArrayList) list).ensureCapacity(minCapacity); } while (minCapacity > list.size()) { list.add(null); } } } private void setLength(Object val) { double d = ScriptRuntime.toNumber(val); long longVal = ScriptRuntime.toUint32(d); if (longVal != d || longVal > Integer.MAX_VALUE) { String msg = ScriptRuntime.getMessageById("msg.arraylength.bad"); throw ScriptRuntime.rangeError(msg); } if (longVal < list.size()) { list.subList((int) longVal, list.size()).clear(); } else { ensureCapacity((int) longVal); } } @Override public Object[] getIds() { List list = (List) javaObject; Object[] result = new Object[list.size()]; int i = list.size(); while (--i >= 0) { result[i] = Integer.valueOf(i); } return result; } private boolean isWithValidIndex(int index) { return index >= 0 && index < list.size(); } @Override public boolean equals(Object obj) { return super.equals(obj); } @Override public int hashCode() { return super.hashCode(); } }