I have a model class that stores keys and values:
public class KeyValue {
private Object key;
private String value;
KeyValue () {
}
KeyValue (Object key, String value) {
this.key=key;
this.value=value;
}
public Object getKey() {
return this.key;
}
public void setKey(Object key) {
this.key=key;
}
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value=value;
}
#Override
public String toString() {
return this.value;
}
}
I use this class to populate a JComboBox's Model:
for (int i = 0; i < universes.length; i++) {
ComboBox_Universes.addItem(new KeyValue(infoObject.ID,infoObject.title));
}
I would like to refactor this logic to use a Java collection class (call it KeyValueCollection) that can support two objectives:
1) the KeyValueCollection can be used to populate the JComboBox's Model. Something like:
//get a KeyValueCollection filled with data from helper class
KeyValueCollection universeCollection = Repository.getUniverseCollection();
//use this collection as the JComboBox's model
ComboBox_Universes.setModel(universeCollection);
2) I can use the KeyValueCollection to convert a key to a value:
//ID retrieve from another control
int universeID = (int)this.Table_Values.getModel().getValueAt(row, COLUMN_ID);
//convert ID to name
String universeName = universeCollection.get(universeID).getValue();
In the .NET world, I would use the KeyedCollection class for this, but I'm not very familiar with Java.
Help is greatly appreciated.
You can use a custom class like this one (run main function to see its behavior) :
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.swing.AbstractListModel;
import javax.swing.ComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
public class KeyValueComboboxModel extends AbstractListModel implements ComboBoxModel, Map<String, String> {
private TreeMap<String,String> values = new TreeMap<String,String>();
private Map.Entry<String, String> selectedItem = null;
public Object getSelectedItem() {
return selectedItem;
}
public void setSelectedItem(Object anItem) {
this.selectedItem = (java.util.Map.Entry<String, String>) anItem;
fireContentsChanged(this, -1, -1);
}
public Object getElementAt(int index) {
List<Map.Entry<String, String>> list = new ArrayList<Map.Entry<String, String>>(values.entrySet());
return list.get(index);
}
public int getSize() {
return values.size();
}
public void clear() {
values.clear();
}
public boolean containsKey(Object key) {
return values.containsKey(key);
}
public boolean containsValue(Object value) {
return values.containsValue(value);
}
public Set<java.util.Map.Entry<String, String>> entrySet() {
return values.entrySet();
}
public String get(Object key) {
return values.get(key);
}
public Set<String> keySet() {
return values.keySet();
}
public String put(String key, String value) {
return values.put(key, value);
}
public String remove(Object key) {
return values.remove(key);
}
public int size() {
return values.size();
}
public Collection<String> values() {
return values.values();
}
public boolean isEmpty() {
return values.isEmpty();
}
public void putAll(Map<? extends String, ? extends String> m) {
values.putAll(m);
}
private static String entryToString(Map.Entry<String, String> entry) {
String str = "" + entry.getKey() + "->" + entry.getValue();
return str;
}
public static void main(String[] args) {
Map<String,String> map= new HashMap<String,String>(){{
put("1","blue");
put("2","red");
put("3","white");
put("4","black");
}};
JFrame f = new JFrame();
f.setContentPane(new JPanel(new BorderLayout()));
KeyValueComboboxModel model = new KeyValueComboboxModel();
model.putAll(map);
final JComboBox combo = new JComboBox(model);
combo.setRenderer(new DefaultListCellRenderer(){
#Override
public Component getListCellRendererComponent(JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus) {
if(value instanceof Map.Entry){
Map.Entry<String,String> entry = (java.util.Map.Entry<String, String>) value;
String str = entryToString(entry);
return super.getListCellRendererComponent(list, str, index, isSelected, cellHasFocus);
}
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
}
});
final JLabel lab = new JLabel("Nothing selected");
combo.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
if(combo.getSelectedItem()!=null){
lab.setText(entryToString((java.util.Map.Entry<String, String>) combo.getSelectedItem()));
} else {
lab.setText("");
}
}
});
f.getContentPane().add(combo,BorderLayout.CENTER);
f.getContentPane().add(lab,BorderLayout.SOUTH);
f.setSize(300,80);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
EDIT : to handle the selected item and keys, you may add these methods:
public void setSelectedKey(String key){
selectedItem = values.ceilingEntry(key);
setSelectedItem(key);
}
public void setSelectedItem(String key, String value){
values.put(key, value);
setSelectedKey(key);
}
By default, values are ordered following the natural order of the keys (alphabetical order of the keys, here, because these are String). If you need an other ordering, add a java.util.Comparator to the TreeMap (see TreeMap documentation).
The Map (implementation HashMap) is a Key-Value class.
It converts from key to value using the method #get.
There are also method to access all keys, all values and so on. So you should have no problem to fill a model with it.
It also contains a Key-Value pair that is called Map.Entry.
Your second requirement suggests that you want a Map, but ComboboxModel is a ListModel, which suggests that you'll want to be able to efficiently retrieve elements by "index".
I don't believe any of the standard collections can do this for you as simply as you'd like. You can either create a Map, and then copy the values to a separate List/ComboboxModel, or you could use something like IndexedList (a List implementation that maintains an index Map).
What about java.util.Map implementations?
with HashMap, for example, you can have:
Map<Object, String> map = new HashMap<Object, String>();
map.put(key, value);
Object value = map.get(key);
However, you can't directly populate the JComboBox with the Map. You can add all keys to the JComboBox, and then get the corresponding values when needed. Adding can be done in many ways, two of which:
new JComboBox(map.keySet().toArray(new Object[]));
by a loop:
for (Object key : map.keySet() {
comboBox.addItem(key);
}
I think that a plain HashMap<Object,String> can address most of your needs:
// Build the map
Map<Object,String> map = new HashMap<Object,String>();
for(InfoObject io : universes)
map.put(io.ID,io.title);
// Populate the ComboBox
for(String s : map.values())
ComboBox_Universes.addItem(s);
// Convert ID to name
int universeID = (int)this.Table_Values.getModel().getValueAt(row, COLUMN_ID);
String universeName = map.get(universeID);
I use the following code:
/**
* This class is slightly modified version of the Pair class from this thread:
* http://stackoverflow.com/questions/156275/what-is-the-equivalent-of-the-c-pairl-r-in-java
* As suggested in the thread above, I have made first & second to be final members.
* I have made it into an Map.Entry<K,V> type, so it is suitable to be an element
* of any Java Hash map...
*
* #author Dejan Lekic - http://dejan.lekic.org
*/
public class Pair<KeyT, ValueT> implements Map.Entry<KeyT, ValueT> {
protected KeyT first;
protected ValueT second;
public Pair(final KeyT argFirst, final ValueT argSecond) {
super();
this.first = argFirst;
this.second = argSecond;
}
#Override
public int hashCode() {
int hashFirst = (first != null) ? first.hashCode() : 0;
int hashSecond = (second != null) ? second.hashCode() : 0;
return (hashFirst + hashSecond) * hashSecond + hashFirst;
}
#Override
public boolean equals(final Object other) {
if (other instanceof Pair) {
Pair otherPair = (Pair) other;
return ((this.first == otherPair.first
|| (this.first != null && otherPair.first != null
&& this.first.equals(otherPair.first)))
&& (this.second == otherPair.second
|| (this.second != null && otherPair.second != null
&& this.second.equals(otherPair.second))));
} // if
return false;
} // equals() method
#Override
public String toString() {
// previously we used " - " as a separator. Now we will use the 0x1f character, called the UNIT
// SEPARATOR to separate two fields in a String object. See the Sise class for more information.
return first + "\u001f" + second;
}
public KeyT getFirst() {
return first;
}
public void setFirst(final KeyT argFirst) {
this.first = argFirst;
}
public ValueT getSecond() {
return second;
}
public void setSecond(final ValueT argSecond) {
this.second = argSecond;
}
#Override
public ValueT setValue(final ValueT argNewValue) {
ValueT oldValue = second;
second = argNewValue;
return oldValue;
}
#Override
public ValueT getValue() {
return second;
}
#Override
public KeyT getKey() {
return first;
}
} // Pair class
// $Id: Pair.java 149 2012-01-13 12:30:59Z dejan $
Related
Hi everyone I want to implement cache map in java in which map entries expire after given time.
I have interface like this, I have to implement these methods, but I am not understand how actually start.
public class CacheMapImpl implements CacheMap<Integer, String> {
#Override
public void setTimeToLive(long timeToLive) {
}
#Override
public long getTimeToLive() {
return 0;
}
#Override
public String put(Integer key, String value) {
return null;
}
#Override
public void clearExpired() {
}
#Override
public void clear() {
}
#Override
public boolean containsKey(Object key) {
return false;
}
#Override
public boolean containsValue(Object value) {
return false;
}
#Override
public String get(Object key) {
return null;
}
#Override
public boolean isEmpty() {
return false;
}
#Override
public String remove(Object key) {
return null;
}
#Override
public int size() {
return 0;
}
}
Please tell me how to implement these methods, how to start write little bit code for me, kindly update my cachemap interface with code.
You have to manage an internal map with the same key. Use your put method to add the new value to your map and also add a value for your internal times' map. You can store a Long as a value, which is the concrete time for that value.
Then, start a new thread in the background that will check all times for all keys in the internal map and remove those that are 'old' entries from both, internal map and your main map.
Here is the code. As I see your Map implements an interface with some methods provided to clear the expired values, I understand you don't need an automatic way to remove expired values. So, the code should be something like:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public class CacheMapImpl implements CacheMap<Integer, String> {
private Map<Integer, Long> timesCache = new HashMap<Integer, Long>();
private Map<Integer, String> values = new HashMap<Integer, String>();
/** Time for the elemens to keep alive in the map in milliseconds. */
long timeToLive = 0;
#Override
public void setTimeToLive(long timeToLive) {
this.timeToLive = timeToLive;
}
#Override
public long getTimeToLive() {
return this.timeToLive;
}
#Override
public String put(Integer key, String value) {
values.put(key, value);
timesCache.put(key, System.currentTimeMillis());
return value;
}
#Override
public void clearExpired() {
// Just remove if timeToLive has been set before...
if (timeToLive > 0) {
List<Integer> keysToClear = new ArrayList<Integer>();
long currentTime = System.currentTimeMillis();
// Check what keys to remove
for (Entry<Integer, Long> e : timesCache.entrySet()) {
if ((currentTime - e.getValue().longValue()) > this.timeToLive) {
keysToClear.add(e.getKey());
}
}
// Remove the expired keys
for (Integer key : keysToClear) {
this.timesCache.remove(key);
this.values.remove(key);
}
}
}
#Override
public void clear() {
this.timesCache.clear();
this.values.clear();
}
#Override
public boolean containsKey(Object key) {
return this.values.containsKey(key);
}
#Override
public boolean containsValue(Object value) {
return this.values.containsValue(value);
}
#Override
public String get(Object key) {
return this.values.get(key);
}
#Override
public boolean isEmpty() {
return this.values.isEmpty();
}
#Override
public String remove(Object key) {
String rto = null;
if (containsKey(key)) {
this.values.remove(key);
this.timesCache.remove(key);
rto = key.toString();
}
return rto;
}
#Override
public int size() {
return this.values.size();
}
}
How about this
package map;
import java.util.Map;
import lombok.Getter;
public class TimeOutCacheMap<K, V> {
Long timeout;
#Getter
private static class MapValue<V> {
private Long timeOut;
private V v;
public MapValue(Long timeout, V v) {
this.timeOut = timeout;
this.v = v;
}
}
public TimeOutCacheMap(Long timeoutInMilliSeconds, Class<? extends Map> mapClazz) {
if (timeoutInMilliSeconds > 5000000) {
throw new RuntimeException("Timeout can be upto 5000000");
}
this.timeout = timeoutInMilliSeconds;
try {
map = mapClazz.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private Map<K, MapValue<V>> map;
public V put(K k, V v) {
if (k == null) {
throw new RuntimeException("Invalid key");
}
Long currentTime = System.currentTimeMillis();
Long timeOutValue = currentTime + this.timeout;
MapValue<V> newV = new MapValue<V>(timeOutValue, v);
MapValue<V> oldV = map.put(k, newV);
return ((oldV == null) ? null : oldV.getV());
}
public V get(K k) {
if (k == null) {
throw new RuntimeException("Invalid key");
}
Long currentTime = System.currentTimeMillis();
MapValue<V> mapValue = map.get(k);
if (mapValue!=null && mapValue.getTimeOut() != null && mapValue.getTimeOut() >= currentTime) {
return mapValue.getV();
} else {
map.remove(k);
return null;
}
}
}
When I do the following:
IMiniMap<String,Integer> map = new SimpleListMM<String,Integer>();
IMiniMap<Double,ArrayList<Object>> map2 = new SimpleListMM<Double,ArrayList<Object>>();
IMiniMap<String,Integer> map = new SimpleListMM<String,Integer>();
then I get an error saying that The constructor SimpleListMM<...,...>() is undefined. I'm not allowed to have setter methods and all I did in constructor is to assign ArrayList<K> smth and ArrayList<V>. Whats the approach for intializing generic constructors in classes? How should I fix that?
import java.util.*;
public class FastGetListMM<K,V> extends AbstractListMM<K,V> implements Comparator<K> {
// Comparator used to sort elements; may be null if elements are Comparable
public final Comparator<K> cmp = new Comparator<K>();
//private List<K> keys;;
//private List<V> values;
// Assume elements must be comparable
public FastGetListMM(ArrayList<K> keys, ArrayList<V> values)
{
super(keys, vals);
this.cmp = new Comparator<K>();
}
// Use the given comparator to sort the keys
public FastGetListMM(Comparator<K> cmp)
{
super(cmp);
//this.cmp = cmp;
}
#Override
public int indexOf(K key) {
return 0;
}
#Override
public V put(K key, V value) {
return null;
}
#Override
public int compare(K arg0, K arg1) {
// TODO Auto-generated method stub
return 0;
}
}
SimpleListMM class:
import java.util.*;
public class SimpleListMM<K,V> extends AbstractListMM<K,V> {
//protected ArrayList<K> keys;
//protected ArrayList<V> vals;
// No special parameters required
public SimpleListMM(ArrayList<K> keys, ArrayList<V> vals)
{
super(keys, vals);
}
// Scan through the list of keys linearly searching for the given
// key. If not present, return a negative number.
public int indexOf(K key)
{
K index = null;
for(int i = 0; i < keys.size(); i++)
{
if(keys.get(i) != key)
return -1;
else
index = keys.get(i);
}
return (Integer) index;
}
// Locate the given key and replace its binding with the given
// value. If not present, add the key and value onto the end of
// their respective lists.
public V put(K key, V value)
{
for(int i = 0; i < keys.size(); i++)
{
if(keys.get(i) == key)
vals.set((Integer)keys.get(i), value);
else
{
keys.add(key);
vals.add(value);
}
}
return (V)vals;
}
}
Your classes don't have a no-arg constructor, thus the compiler error. Add the proper constructor in both classes:
public class SimpleListMM<K,V> extends AbstractListMM<K,V> {
public SimpleListMM() {
//some initialization logic
//maybe like this
super(new ArrayList<K>(), new ArrayList<V>());
}
public SimpleListMM(ArrayList<K> keys, ArrayList<V> vals) {
super(keys, vals);
}
}
I've created a hashmap with .class objects for keys.
Hashmap<Class<? extends MyObject>, Object> mapping = new Hashmap<Class<? extends MyObject>, Object>();
This is all well and fine, but I'm getting strange behaviour that I can only attribute to strangeness with the hash function. Randomly during runtime, iterating through the hashmap will not hit every value; it will miss one or two. I think this may be due to the .class object not being final, and therefore it changes causing it to map to a different hash value. With a different hash value, the hashmap wouldn't be able to correctly correlate the key with the value, thus making it appear to have lost the value.
Am I correct that this is what is going on? How can I work around this? Is there a better way to accomplish this form of data structure?
Edit: I really thought I was onto something with the hash function thing, but I'll post my real code to try and figure this out. It may be a problem with my implementation of a multimap. I've been using it for quite some time and haven't noticed any issues until recently.
/**
* My own implementation of a map that maps to a List. If the key is not present, then
* the map adds a List with a single entry. Every subsequent addition to the key
* is appended to the List.
* #author
*
* #param <T> Key
* #param <K> Value
*/
public class MultiMap<T, K> implements Map<T, List<K>>, Serializable, Iterable<K> {
/**
*
*/
private static final long serialVersionUID = 5789101682525659411L;
protected HashMap<T, List<K>> set = new HashMap<T, List<K>>();
#Override
public void clear() {
set = new HashMap<T, List<K>>();
}
#Override
public boolean containsKey(Object arg0) {
return set.containsKey(arg0);
}
#Override
public boolean containsValue(Object arg0) {
boolean output = false;
for(Iterator<List<K>> iter = set.values().iterator();iter.hasNext();) {
List<K> searchColl = iter.next();
for(Iterator<K> iter2 = searchColl.iterator(); iter2.hasNext();) {
K value = iter2.next();
if(value == arg0) {
output = true;
break;
}
}
}
return output;
}
#Override
public Set<Entry<T, List<K>>> entrySet() {
Set<Entry<T, List<K>>> output = new HashSet<Entry<T,List<K>>>();
for(Iterator<T> iter1 = set.keySet().iterator(); iter1.hasNext();) {
T key = iter1.next();
for(Iterator<K> iter2 = set.get(key).iterator(); iter2.hasNext();) {
K value = iter2.next();
List<K> input = new ArrayList<K>();
input.add(value);
output.add(new AbstractMap.SimpleEntry<T,List<K>>(key, input));
}
}
return output;
}
#Override
public boolean isEmpty() {
return set.isEmpty();
}
#Override
public Set<T> keySet() {
return set.keySet();
}
#Override
public int size() {
return set.size();
}
#Override
public Collection<List<K>> values() {
Collection<List<K>> values = new ArrayList<List<K>>();
for(Iterator<T> iter1 = set.keySet().iterator(); iter1.hasNext();) {
T key = iter1.next();
values.add(set.get(key));
}
return values;
}
#Override
public List<K> get(Object key) {
return set.get(key);
}
#Override
public List<K> put(T key, List<K> value) {
return set.put(key, value);
}
public void putValue(T key, K value) {
if(set.containsKey(key)) {
set.get(key).add(value);
}
else {
List<K> setval = new ArrayList<K>();
setval.add(value);
set.put(key, setval);
}
}
#Override
public List<K> remove(Object key) {
return set.remove(key);
}
public K removeValue(Object value) {
K valueRemoved = null;
for(T key:this.keySet()) {
for(K val:this.get(key)) {
if(val.equals(value)) {
List<K> temp = this.get(key);
temp.remove(value);
valueRemoved = val;
this.put(key, temp);
}
}
}
return valueRemoved;
}
#Override
public void putAll(Map<? extends T, ? extends List<K>> m) {
for(Iterator<? extends T> iter = m.keySet().iterator(); iter.hasNext();) {
T key = iter.next();
set.put(key, m.get(key));
}
}
#Override
public Iterator<K> iterator() {
return new MultiMapIterator<K>(this);
}
}
Perhaps there is an issue with my iterator? I'll post that code as well.
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
public class MultiMapIterator<T> implements Iterator<T> {
private MultiMap <?, T> map;
private Iterator<List<T>> HashIter;
private Iterator<T> govIter;
private T value;
public MultiMapIterator(MultiMap<?, T> map) {
this.map = map;
HashIter = map.values().iterator();
if(HashIter.hasNext()) {
govIter = HashIter.next().iterator();
}
if(govIter.hasNext()) {
value = govIter.next();
}
}
#Override
public boolean hasNext() {
if (govIter.hasNext()) {
return true;
}
else if(HashIter.hasNext()) {
govIter = HashIter.next().iterator();
return this.hasNext();
}
else {
return false;
}
}
#Override
public T next() {
if(!this.hasNext()) {
throw new NoSuchElementException();
}
else {
value = govIter.next();
return value;
}
}
#Override
public void remove() {
map.remove(value);
}
}
Sorry for the long tracts of code. Thank you for spending time helping me with this.
You pull the a value out of govIter in the constructor, but never return it.
Your iterator remove method is completely wrong. You are iterating values, but calling the map.remove which removes by key. you simply want to call govIter.remove() (unless you need to avoid empty lists, in which case it's more complicated).
Your hasNext() method could also have problems depending on whether or not you allow empty Lists values in your multimap.
I have ArrayList which contains a value and index, how can i sort the value without sorting the index?
example:
ArrayList<Integer> arr = new ArrayList<Integer>();
arr.add(1,100);
arr.add(2,50);
arr.add(3,10);
the result will be {(3,10),(2,50),(1,100)}
thanks :D
Besides the Map option, you can consider using some sort of Key value pair and, storing that in your array and sort your array based on the values.
See this for potential key value pairs, or make your own.
An example:
package com.example;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class KeyValue {
private int key;
private Integer value;
public KeyValue(int i, Integer j) {
key = i;
value = j;
}
public static void main(String[] args) {
List<KeyValue> arr = new ArrayList<KeyValue>();
arr.add(new KeyValue(1, 100));
arr.add(new KeyValue(2, 50));
arr.add(new KeyValue(3, 10));
Collections.sort(arr, new Comparator<KeyValue>(){
#Override
public int compare(KeyValue arg0, KeyValue arg1) {
return Integer.compare(arg0.getValue(), arg1.getValue());
}
});
for (KeyValue kv : arr){
System.out.println(kv);
}
}
public int getKey() {
return key;
}
public void setKey(int key) {
this.key = key;
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
#Override
public String toString(){
return "("+key+","+value.toString()+")";
}
}
What you're asking doesn't make sense because an element's index is directly tied to how it is sorted. You should use a Map or something similar.
I'm starting to use the dynamic rhinoscript feature in Java 6 for use by customers who are more likely to know Javascript than Java.
What is the best way to pass a Map (associative array, javascript obj, whatever) into Javascript so the script-writers can use the standard Javascript dot notation for accessing values?
I'm currently passing a java.util.Map of values into the script, however then the script writer has to write "map.get('mykey')" instead of "map.mykey".
Basically, I want to do the opposite of this question.
I took the Java NativeObject approach and here is what I did...
// build a Map
Map<String, String> map = new HashMap<String, String>();
map.put("bye", "now");
// Convert it to a NativeObject (yes, this could have been done directly)
NativeObject nobj = new NativeObject();
for (Map.Entry<String, String> entry : map.entrySet()) {
nobj.defineProperty(entry.getKey(), entry.getValue(), NativeObject.READONLY);
}
// Get Engine and place native object into the context
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("javascript");
engine.put("map", nobj);
// Standard Javascript dot notation prints 'now' (as it should!)
engine.eval("println(map.bye);");
I am using an utility class that convert Map into javascript hash object:
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.mozilla.javascript.Scriptable;
public class MapScriptable implements Scriptable, Map {
public final Map map;
public MapScriptable(Map map) {
this.map = map;
}
public void clear() {
map.clear();
}
public boolean containsKey(Object key) {
return map.containsKey(key);
}
public boolean containsValue(Object value) {
return map.containsValue(value);
}
public Set entrySet() {
return map.entrySet();
}
public boolean equals(Object o) {
return map.equals(o);
}
public Object get(Object key) {
return map.get(key);
}
public int hashCode() {
return map.hashCode();
}
public boolean isEmpty() {
return map.isEmpty();
}
public Set keySet() {
return map.keySet();
}
public Object put(Object key, Object value) {
return map.put(key, value);
}
public void putAll(Map m) {
map.putAll(m);
}
public Object remove(Object key) {
return map.remove(key);
}
public int size() {
return map.size();
}
public Collection values() {
return map.values();
}
#Override
public void delete(String name) {
map.remove(name);
}
#Override
public void delete(int index) {
map.remove(index);
}
#Override
public Object get(String name, Scriptable start) {
return map.get(name);
}
#Override
public Object get(int index, Scriptable start) {
return map.get(index);
}
#Override
public String getClassName() {
return map.getClass().getName();
}
#Override
public Object getDefaultValue(Class<?> hint) {
return toString();
}
#Override
public Object[] getIds() {
Object[] res=new Object[map.size()];
int i=0;
for (Object k:map.keySet()) {
res[i]=k;
i++;
}
return res;
}
#Override
public Scriptable getParentScope() {
return null;
}
#Override
public Scriptable getPrototype() {
return null;
}
#Override
public boolean has(String name, Scriptable start) {
return map.containsKey(name);
}
#Override
public boolean has(int index, Scriptable start) {
return map.containsKey(index);
}
#Override
public boolean hasInstance(Scriptable instance) {
return false;
}
#Override
public void put(String name, Scriptable start, Object value) {
map.put(name, value);
}
#Override
public void put(int index, Scriptable start, Object value) {
map.put(index, value);
}
#Override
public void setParentScope(Scriptable parent) {}
#Override
public void setPrototype(Scriptable prototype) {}
}
Sample:
import java.util.HashMap;
import java.util.Map;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ScriptableObject;
public class MapScriptableMain {
public static void main(String[] args) {
Map src=new HashMap();
src.put("foo", 2);
src.put("bar", 3);
MapScriptable m=new MapScriptable(src);
Context c=Context.enter();
ScriptableObject scope = c.initStandardObjects();
ScriptableObject.putProperty(scope, "m", m);
String source = "m.baz=m.foo+m.bar;";
Object a=c.evaluateString(scope, source, "TEST", 1, null);
System.out.println(a); // 5.0
System.out.println(src.get("baz")); // 5.0;
}
}
After figuring out that the SimpleScriptContext will only take the Map object and thus force you to use the Java methods in your JavaScript, here's what I did.
Map<String, String> myMap = new HashMap<String, String>();
myMap.put("test", "hello world!");
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
Object eval = engine.eval("var map = " + new Gson().toJson(myMap) + ";\n"
+ "println(map.test);");
Which printed out
hello world!
You just need to encode your object as JSON, either manually, or using a library like Jackson or gson. As you said, it's the exact oposite of that question and the author of that question is not happy with the JSON notation :)
What you need to send to the browser is basically something like this:
var someObject = { "key1": "value1", "key2": "value2", ... }
And then the javascript developer can simply access: someObject.key2.