/*
* Copyright 2016 Nabarun Mondal
* 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.noga.njexl.lang.extension.oop;
import com.noga.njexl.lang.extension.TypeUtility;
import com.noga.njexl.lang.Interpreter;
import com.noga.njexl.lang.introspection.JexlMethod;
import com.noga.njexl.lang.introspection.JexlPropertyGet;
import com.noga.njexl.lang.introspection.JexlPropertySet;
import com.noga.njexl.lang.introspection.UberspectImpl;
import com.noga.njexl.lang.extension.oop.ScriptClassBehaviour.*;
import java.lang.reflect.Field;
import java.util.HashMap;
/**
* An instance of nJexl class
* Created by noga on 09/04/15.
*/
public class ScriptClassInstance implements Executable, Comparable,Arithmetic, Logic, Eventing {
final HashMap<String, Object> fields;
/**
* gets declared fields
* @return declared fields of the object
*/
public HashMap<String,Object> getFields(){return fields ;}
final HashMap<String,Object> supers;
protected void addSuper(ScriptClassInstance value){
if ( $.ns.equals(value.$.ns)) {
//make direct calling possible ONLY if the namespace of child and parent match
supers.put(value.$.name, value);
}
supers.put( value.$.ns +":" + value.$.name, value);
}
/**
* Gets the immediate super classes
* @return immediate super class instances
*/
public HashMap<String,Object> getSupers(){ return supers ; }
Interpreter interpreter;
final ScriptClass $;
public ScriptClass getNClass(){ return $;}
public ScriptClassInstance(ScriptClass scriptClass, Interpreter interpreter){
this.interpreter = interpreter ;
this.$ = scriptClass ;
this.fields = new HashMap<>();
this.supers = new HashMap<>();
}
public JexlMethod getJSuperMethod(String name, Object[] sups, Object[] args) throws Exception {
for ( String s : supers.keySet() ){
if ( s.contains(":") ){
continue;
}
Object sup = supers.get(s);
if ( !(sup instanceof ScriptClassInstance) ){
JexlMethod method = interpreter.uberspect.getMethod(sup, name, args, null);
if ( method != null ){
sups[0] = sup;
return method ;
}
}
}
return null;
}
public void addJSuper(String name, Object obj, Object[] args) throws Exception {
JexlMethod ctor = interpreter.uberspect.getConstructorMethod(obj, args, null);
// DG: If we can't find an exact match, narrow the parameters and try again
if (ctor == null) {
if (interpreter.arithmetic.narrowArguments(args)) {
ctor = interpreter.uberspect.getConstructorMethod(obj, args, null);
if (ctor == null) {
throw new Exception("Constructor not found!");
}
}
}
Object instance = ctor.invoke(null, args);
supers.put(name,instance);
supers.put($.ns + ":" + name,instance);
}
public void ancestor(Object sName,Object[]args) throws Exception {
Object old = interpreter.resolveJexlClassName(sName.toString());
if ( old != null && ((ScriptClass)old).clazz == null ) {
ScriptClassInstance _new_ = ((ScriptClass)old).instance(interpreter, args);
addSuper( _new_ ); // this should replace the old
return;
}
String name;
if ( sName instanceof String ){
sName = interpreter.getContext().get((String)sName);
}
if ( sName instanceof Class ){
old = ((Class) sName).getName();
name = ((Class) sName).getSimpleName();
}else{
old = sName.getClass().getName();
name = sName.getClass().getSimpleName();
}
// java guy... call constructor
addJSuper(name,old,args);
}
@Override
public Object execMethod(String method, Interpreter i, Object[] args) {
try {
if ( method.equals(ScriptClass._ANCESTOR_)){
Object arg0 = args[0];
args = TypeUtility.shiftArrayLeft(args, 1);
ancestor(arg0,args);
return null;
}
ScriptMethod methodDef = $.getMethod(method);
if (methodDef == null ) {
Object[] sups = new Object[1];
JexlMethod jexlMethod = getJSuperMethod(method, sups, args);
if (jexlMethod != null) {
// call jexl method
return jexlMethod.invoke(sups[0], args);
}
throw new NoSuchMethodException("Method : '" + method + "' is not found in class : " + this.$.name);
}
if (methodDef.instance) {
return methodDef.invoke(this, interpreter, args);
}
return methodDef.invoke(null, interpreter, args);
}catch (Throwable e){
throw new Error(e);
}
}
public Object get(String name) throws Exception {
if (fields.containsKey(name)) {
return fields.get(name);
}
for (String sup : supers.keySet()) {
Object s = supers.get(sup);
if (s instanceof ScriptClassInstance ) {
ScriptClassInstance supI = (ScriptClassInstance)s;
if ( supI.fields.containsKey(name)){
return supI.fields.get(name);
}
}else{
// try from this
Field field = ((UberspectImpl)interpreter.uberspect).getField(s, name, null);
if (field != null) {
JexlPropertyGet fg = new UberspectImpl.FieldPropertyGet(field);
return fg.invoke(s);
}
}
}
try {
// try finding functions...?
ScriptMethod fp = $.getMethod(name);
Object o = new ScriptClass.MethodInstance(this,fp.name);
// cache it -- important
fields.put(name,o);
return o;
}catch (Exception e){
// nothing...
}
throw new Exception("Key : '" + name + "' is not found!");
}
public void set(String name, Object value) throws Exception {
if (fields.containsKey(name)) {
fields.put(name, value);
return;
}
for (String sup : supers.keySet()) {
Object s = supers.get(sup);
if (s instanceof ScriptClassInstance ) {
ScriptClassInstance supI = (ScriptClassInstance)s;
if ( supI.fields.containsKey(name)){
supI.fields.put(name,value);
return;
}
}else{
Field field = ((UberspectImpl)interpreter.uberspect).getField(s, name, null);
if (field != null) {
JexlPropertySet fs = new UberspectImpl.FieldPropertySet(field);
fs.invoke(s,value);
return;
}
}
}
fields.put(name, value);
}
@Override
public String toString() {
try {
return execMethod(ScriptClassBehaviour.STR, interpreter ,new Object[]{}).toString();
} catch (Throwable e) {
}
return super.toString();
}
@Override
public boolean equals(Object o) {
try {
return TypeUtility.castBoolean(execMethod(ScriptClassBehaviour.EQ,interpreter , new Object[]{o}), false);
} catch (Throwable e) {
}
return super.equals(o);
}
@Override
public int hashCode() {
try {
return TypeUtility.castInteger(execMethod(ScriptClassBehaviour.HC, interpreter , new Object[]{}));
} catch (Throwable e) {
}
return super.hashCode();
}
@Override
public int compareTo(Object o) {
return TypeUtility.castInteger(execMethod(ScriptClassBehaviour.CMP,interpreter, new Object[]{o}));
}
@Override
public Object add(Object o) {
return execMethod(Arithmetic.ADD,interpreter,new Object[]{o});
}
@Override
public Object neg() {
return execMethod(Arithmetic.NEG,interpreter,new Object[]{});
}
@Override
public Object sub(Object o) {
return execMethod(Arithmetic.SUB, interpreter , new Object[]{o});
}
@Override
public Object mul(Object o) {
return execMethod(Arithmetic.MUL, interpreter , new Object[]{o});
}
@Override
public Object div(Object o) {
return execMethod(Arithmetic.DIV, interpreter , new Object[]{o});
}
@Override
public Object exp(Object o) {
return execMethod(Arithmetic.EXP, interpreter , new Object[]{o});
}
@Override
public Object and(Object o) {
return execMethod(Logic.AND,interpreter, new Object[]{o});
}
@Override
public Object complement() {
return execMethod(Logic.COMPLEMENT,interpreter ,new Object[]{});
}
@Override
public Object or(Object o) {
return execMethod(Logic.OR,interpreter ,new Object[]{o});
}
@Override
public Object xor(Object o) {
return execMethod(Logic.XOR,interpreter , new Object[]{o});
}
@Override
public void before(Event event) {
try{
Object[] params = new Object[]{ event } ;
execMethod(BEFORE,interpreter ,params );
}catch (Throwable t){
if ( t.getCause() instanceof NoSuchMethodException ) {
Timer.TIMER.before(event);
}
}
}
@Override
public void after(Event event) {
try{
Object[] params = new Object[]{ event } ;
execMethod(AFTER,interpreter,params );
}catch (Throwable t){
if ( t.getCause() instanceof NoSuchMethodException ) {
Timer.TIMER.after(event);
}
}
}
}