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

org.htmlunit.corejs.javascript.NativeJavaMap 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.Iterator;
import java.util.List;
import java.util.Map;

/**
 * NativeJavaMap is a wrapper for java objects implementing java.util.Map
 *  interface. When {@link Context#FEATURE_ENABLE_JAVA_MAP_ACCESS} is enabled, property based
 * access like map[key] is delegated to {@link Map#get(Object)} or {@link
 * Map#put(Object, Object)} operations so that a JavaMap acts very similar to a
 * javascript Object There is also an iterator to iterate over entries with 
 * for .. of.
 *
 * 

Limitations: The wrapped map should have String or Integer as * key. Otherwise, property based access may not work properly. */ public class NativeJavaMap extends NativeJavaObject { private static final long serialVersionUID = -3786257752907047381L; private Map map; static void init(ScriptableObject scope, boolean sealed) { NativeJavaMapIterator.init(scope, sealed); } @SuppressWarnings("unchecked") public NativeJavaMap(Scriptable scope, Object map) { super(scope, map, map.getClass()); assert map instanceof Map; this.map = (Map) map; } @Override public String getClassName() { return "JavaMap"; } @Override public boolean has(String name, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { if (map.containsKey(name)) { return true; } } return super.has(name, start); } @Override public boolean has(int index, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { if (map.containsKey(Integer.valueOf(index))) { return true; } } return super.has(index, start); } @Override public boolean has(Symbol key, Scriptable start) { if (SymbolKey.ITERATOR.equals(key)) { return true; } return false; } @Override public Object get(String name, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { if (map.containsKey(name)) { Object obj = map.get(name); return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass()); } } return super.get(name, start); } @Override public Object get(int index, Scriptable start) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { if (map.containsKey(Integer.valueOf(index))) { Object obj = map.get(Integer.valueOf(index)); return cx.getWrapFactory().wrap(cx, this, obj, obj == null ? null : obj.getClass()); } } return super.get(index, start); } @Override public Object get(Symbol key, Scriptable start) { if (SymbolKey.ITERATOR.equals(key)) { return symbol_iterator; } return super.get(key, start); } @Override public void put(String name, Scriptable start, Object value) { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { map.put(name, Context.jsToJava(value, Object.class)); } else { super.put(name, start, value); } } @Override public void put(int index, Scriptable start, Object value) { Context cx = Context.getContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { map.put(Integer.valueOf(index), Context.jsToJava(value, Object.class)); } else { super.put(index, start, value); } } @Override public Object[] getIds() { Context cx = Context.getCurrentContext(); if (cx != null && cx.hasFeature(Context.FEATURE_ENABLE_JAVA_MAP_ACCESS)) { List ids = new ArrayList<>(map.size()); for (Object key : map.keySet()) { if (key instanceof Integer) { ids.add(key); } else { ids.add(ScriptRuntime.toString(key)); } } return ids.toArray(); } return super.getIds(); } @Override public boolean equals(Object obj) { return super.equals(obj); } @Override public int hashCode() { return super.hashCode(); } private static Callable symbol_iterator = (Context cx, Scriptable scope, Scriptable thisObj, Object[] args) -> { if (!(thisObj instanceof NativeJavaMap)) { throw ScriptRuntime.typeErrorById("msg.incompat.call", SymbolKey.ITERATOR); } return new NativeJavaMapIterator(scope, ((NativeJavaMap) thisObj).map); }; private static final class NativeJavaMapIterator extends ES6Iterator { private static final long serialVersionUID = 1L; private static final String ITERATOR_TAG = "JavaMapIterator"; static void init(ScriptableObject scope, boolean sealed) { ES6Iterator.init(scope, sealed, new NativeJavaMapIterator(), ITERATOR_TAG); } /** Only for constructing the prototype object. */ private NativeJavaMapIterator() { super(); } NativeJavaMapIterator(Scriptable scope, Map map) { super(scope, ITERATOR_TAG); this.iterator = map.entrySet().iterator(); } @Override public String getClassName() { return "Java Map Iterator"; } @Override protected boolean isDone(Context cx, Scriptable scope) { return !iterator.hasNext(); } @Override protected Object nextValue(Context cx, Scriptable scope) { if (!iterator.hasNext()) { return cx.newArray(scope, new Object[] {Undefined.instance, Undefined.instance}); } Map.Entry e = iterator.next(); Object key = e.getKey(); Object value = e.getValue(); WrapFactory wrapFactory = cx.getWrapFactory(); key = wrapFactory.wrap(cx, this, key, key == null ? null : key.getClass()); value = wrapFactory.wrap(cx, this, value, value == null ? null : value.getClass()); return cx.newArray(scope, new Object[] {key, value}); } @Override protected String getTag() { return ITERATOR_TAG; } private Iterator> iterator; } }