/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option) any
later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along
with this program; if not, see http://www.gnu.org/licenses or write to the Free
Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
*/
package com.servoy.j2db.scripting;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.NativeJavaArray;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.WrappedException;
import com.servoy.j2db.Messages;
import com.servoy.j2db.util.IDestroyable;
import com.servoy.j2db.util.Utils;
/**
* @author jcompagner
*/
public abstract class DefaultScope implements Scriptable, IDestroyable
{
private volatile Scriptable parent;
private volatile Scriptable prototype;
protected volatile HashMap<String, Object> allVars; //name -> object
protected volatile HashMap<Integer, Object> allIndex; //index -> object
protected boolean locked = false;
public DefaultScope(Scriptable parent)
{
this.parent = parent;
allVars = new HashMap<String, Object>();
allIndex = new HashMap<Integer, Object>();
}
/**
* @see org.mozilla.javascript.Scriptable#getClassName()
*/
public String getClassName()
{
return getClass().getSimpleName();
}
/**
* @see org.mozilla.javascript.Scriptable#get(java.lang.String, org.mozilla.javascript.Scriptable)
*/
public Object get(String name, Scriptable start)
{
if ("length".equals(name)) //$NON-NLS-1$
{
return new Integer(allIndex.size());
}
else if ("allnames".equals(name)) //$NON-NLS-1$
{
Context.enter();
try
{
Object[] array = allVars.keySet().toArray(new String[allVars.size()]);
Arrays.sort(array);
return new NativeJavaArray(this, array);
}
finally
{
Context.exit();
}
}
Object o = allVars.get(name);
if (o == null && !has(name, start)) return Scriptable.NOT_FOUND;
if (o != null && o != Scriptable.NOT_FOUND && !(o instanceof Scriptable))
{
Context context = Context.getCurrentContext();
if (context != null) o = context.getWrapFactory().wrap(context, start, o, o.getClass());
}
return o;
}
/**
* @see org.mozilla.javascript.Scriptable#get(int, org.mozilla.javascript.Scriptable)
*/
public Object get(int index, Scriptable start)
{
Object o = allIndex.get(new Integer(index));
if (o == null && !has(index, start)) return Scriptable.NOT_FOUND;
return o;
}
/**
* @see org.mozilla.javascript.Scriptable#has(java.lang.String, org.mozilla.javascript.Scriptable)
*/
public boolean has(String name, Scriptable start)
{
if (name != null && name.equals("length")) return true; //$NON-NLS-1$
return allVars.containsKey(name);
}
/**
* @see org.mozilla.javascript.Scriptable#has(int, org.mozilla.javascript.Scriptable)
*/
public boolean has(int index, Scriptable start)
{
return allIndex.containsKey(new Integer(index));
}
/**
* @see org.mozilla.javascript.Scriptable#put(java.lang.String, org.mozilla.javascript.Scriptable, java.lang.Object)
*/
public void put(String name, Scriptable start, Object value)
{
if (locked) throw new WrappedException(new RuntimeException(Messages.getString("servoy.javascript.error.lockedForName", new Object[] { name, value }))); //$NON-NLS-1$
allVars.put(name, value);
}
/**
* @see org.mozilla.javascript.Scriptable#put(int, org.mozilla.javascript.Scriptable, java.lang.Object)
*/
public void put(int index, Scriptable start, Object value)
{
if (locked) throw new WrappedException(new RuntimeException(Messages.getString(
"servoy.javascript.error.lockedForIndex", new Object[] { new Integer(index), value }))); //$NON-NLS-1$
allIndex.put(new Integer(index), value);
}
/**
* @see org.mozilla.javascript.Scriptable#delete(java.lang.String)
*/
public void delete(String name)
{
if (locked) throw new WrappedException(new RuntimeException(Messages.getString("servoy.javascript.error.lockedForDeleteName", new Object[] { name }))); //$NON-NLS-1$
allVars.remove(name);
}
/**
* @see org.mozilla.javascript.Scriptable#delete(int)
*/
public void delete(int index)
{
if (locked) throw new WrappedException(new RuntimeException(Messages.getString(
"servoy.javascript.error.lockedForDeleteIndex", new Object[] { new Integer(index) }))); //$NON-NLS-1$
allIndex.remove(new Integer(index));
}
/**
* @see org.mozilla.javascript.Scriptable#getPrototype()
*/
public Scriptable getPrototype()
{
return prototype;
}
/**
* @see org.mozilla.javascript.Scriptable#setPrototype(org.mozilla.javascript.Scriptable)
*/
public void setPrototype(Scriptable prototype)
{
this.prototype = prototype;
}
/**
* @see org.mozilla.javascript.Scriptable#getParentScope()
*/
public Scriptable getParentScope()
{
return parent;
}
/**
* @see org.mozilla.javascript.Scriptable#setParentScope(org.mozilla.javascript.Scriptable)
*/
public void setParentScope(Scriptable parent)
{
this.parent = parent;
}
/**
* @see org.mozilla.javascript.Scriptable#getIds()
*/
public Object[] getIds()
{
Object[] array = new Object[allVars.size() + allIndex.size() + 2];
int counter = 0;
array[counter++] = "allnames"; //$NON-NLS-1$
array[counter++] = "length"; //$NON-NLS-1$
for (String string : allVars.keySet())
{
array[counter++] = string;
}
for (Integer integer : allIndex.keySet())
{
array[counter++] = integer;
}
return array;
}
public Object[] getValues()
{
return allVars.values().toArray();
}
/**
* @see org.mozilla.javascript.Scriptable#getDefaultValue(java.lang.Class)
*/
public Object getDefaultValue(Class< ? > typeHint)
{
StringBuilder sb = new StringBuilder();
sb.append(getClassName());
sb.append('[');
Object[] objects = getIds();
for (Object element : objects)
{
sb.append(element);
sb.append(","); //$NON-NLS-1$
}
if (objects.length > 0) sb.setCharAt(sb.length() - 1, ']');
else sb.append(']');
return sb.toString();
}
/**
* @see org.mozilla.javascript.Scriptable#hasInstance(org.mozilla.javascript.Scriptable)
*/
public boolean hasInstance(Scriptable instance)
{
Scriptable proto = instance.getPrototype();
while (proto != null)
{
if (proto.equals(this)) return true;
proto = proto.getPrototype();
}
return false;
}
/**
* @return
*/
public boolean isLocked()
{
return locked;
}
/**
* @param b
*/
public void setLocked(boolean b)
{
locked = b;
}
public void destroy()
{
this.allIndex.clear();
this.allVars.clear();
this.parent = null;
this.prototype = null;
}
/**
* Method used to remove a method or a variable from the {@link FormScope}
* @param name
*/
public void remove(String name)
{
boolean found = false;
Object o = allVars.remove(name);
if (o != null)
{
Iterator<Entry<Integer, Object>> it = allIndex.entrySet().iterator();
while (it.hasNext())
{
Map.Entry<Integer, Object> entry = it.next();
if (entry.getValue().equals(name))
{
Integer key = entry.getKey();
allIndex.remove(key);
Integer nextKey = new Integer(key.intValue() + 1);
o = allIndex.remove(nextKey);
while (o != null)
{
allIndex.put(key, o);
key = nextKey;
nextKey = new Integer(key.intValue() + 1);
o = allIndex.remove(nextKey);
}
found = true;
break;
}
}
if (!found)
{
Utils.mapRemoveByValue(name, allIndex);
}
}
}
}