/*
* $Id: ParameterListDeclaration.java,v 1.22 2002/09/16 08:05:03 jkl Exp $
*
* Copyright (c) 2002 Njet Communications Ltd. All Rights Reserved.
*
* Use is subject to license terms, as defined in
* Anvil Sofware License, Version 1.1. See LICENSE
* file, or http://njet.org/license-1.1.txt
*/
package anvil.script;
import java.io.IOException;
import java.util.Enumeration;
import java.lang.reflect.Method;
import anvil.ErrorListener;
import anvil.core.Any;
import anvil.core.AnyTuple;
import anvil.core.Array;
import anvil.doc.Doc;
import anvil.script.expression.Expression;
import anvil.script.statements.FunctionStatement;
import anvil.script.compiler.ByteCompiler;
import anvil.codec.ClassRoom;
import anvil.codec.Code;
import anvil.codec.ConstantPool;
import anvil.codec.CodecConstants;
/**
* class ParameterListDeclaration
*
* @author: Jani Lehtim�ki
*/
public class ParameterListDeclaration implements CodecConstants
{
public static final ParameterListDeclaration EMPTY = new ParameterListDeclaration(0).open().close();
protected static class Parameter
{
protected int type;
protected String name;
protected Any value;
protected Expression expression;
protected boolean required = true;
protected Doc doc;
}
private static final Integer ZERO = new Integer(0);
private static final Long LONG_ZERO = new Long(0);
private static final Double ZERO_POINT_ZERO = new Double(0.0);
protected int _size = 0;
protected int _minsize = 0;
protected Parameter[] _params = null;
public ParameterListDeclaration()
{
this(4);
}
public ParameterListDeclaration(int initialSize)
{
_params = new Parameter[initialSize];
}
public ParameterListDeclaration(Method method, Object[] types, Doc document)
{
Class params[] = method.getParameterTypes();
int plength = params.length;
int dptr = 0;
int dlength = (types != null) ? types.length : 0;
boolean hadminsize = false;
_params = new Parameter[plength];
_size = plength;
if (dlength>0 && types[0] instanceof Integer) {
_minsize = ((Integer)types[0]).intValue();
hadminsize = true;
dptr++;
}
if (plength>0) {
String[] classes = CompilableFunction.CLASSES;
for(int i=0; i<plength; i++) {
int type = -1;
String from = params[i].getName();
for(int c=0; c<classes.length; c++) {
String to = classes[c];
if (from.equals(to)) {
type = c;
break;
}
}
if (type == -1) {
throw new RuntimeException("Unsupported parameter type detected on '" +
method.getName() + "' prototype: " + from);
}
Parameter param = new Parameter();
param.type = type;
if (dptr<dlength) {
String name = (String)types[dptr++];
if (name == null) {
name = "$" + i;
}
if (name.startsWith("..")) {
param.type = CompilableFunction.PARAMETER_REST;
param.name = name.substring(1);
param.required = false;
if (dptr < dlength) {
Object o = types[dptr++];
if (o != null) {
param.value = Any.create(o);
}
}
} else if (name.startsWith("*")) {
param.name = name.substring(1);
param.required = false;
if (dptr < dlength) {
Object o = types[dptr++];
if (o != null) {
param.value = Any.create(o);
}
}
} else {
param.name = name;
}
} else {
param.name = "$" + i;
}
_params[i] = param;
}
}
if (!hadminsize) {
close();
}
importDocuments(document);
}
private void expandCapacity()
{
int length = _params.length;
Parameter[] params = new Parameter[length + 4];
System.arraycopy(_params, 0, params, 0, length);
_params = params;
}
public String toString()
{
return toString(new StringBuffer(48)).toString();
}
public StringBuffer toString(StringBuffer buffer)
{
int n = _size;
boolean first = true;
Parameter[] params = _params;
for(int i=0; i<n; i++) {
Parameter param = params[i];
int t = param.type;
if (t == CompilableFunction.PARAMETER_CONTEXT) {
continue;
}
if (t >= CompilableFunction.PARAMETER_ARRAY) {
if (!first) {
buffer.append(", ");
} else {
first = false;
}
buffer.append("..");
buffer.append(param.name);
} else {
if (!first) {
buffer.append(", ");
} else {
first = false;
}
if (!param.required) {
buffer.append('*');
}
buffer.append(param.name);
}
}
return buffer;
}
public int size()
{
return _size;
}
public int minSize()
{
return _minsize;
}
public boolean isDeclared(String name)
{
Parameter[] params = _params;
for(int i = _size-1; i>=0; i--) {
if (params[i].equals(name)) {
return true;
}
}
return false;
}
public boolean hasDefaultValues()
{
Parameter[] params = _params;
for(int i = _size-1; i>=0; i--) {
if (!params[i].required) {
return true;
}
}
return false;
}
public String getName(int index)
{
if ((index >= 0) && (index < _size)) {
return _params[index].name;
} else {
return "$" + index;
}
}
public int getType(int index)
{
if ((index >= 0) && (index < _size)) {
return _params[index].type;
} else {
return 0;
}
}
public Any getDefault(int index)
{
if ((index >= 0) && (index < _size)) {
return _params[index].value;
}
return null;
}
public boolean getRequired(int index)
{
if ((index >= 0) && (index < _size)) {
return _params[index].required;
} else {
return true;
}
}
public Doc getDoc(int index)
{
if ((index >= 0) && (index < _size)) {
return _params[index].doc;
} else {
return null;
}
}
public void add(String name)
{
add(CompilableFunction.PARAMETER_ANY, name, null, null);
}
public void add(String name, Any value)
{
add(CompilableFunction.PARAMETER_ANY, name, value, null);
}
public void add(String name, Expression expr)
{
add(CompilableFunction.PARAMETER_ANY, name, (expr != null) ? Any.UNDEFINED : null, expr);
}
public void add(int type, String name, Any value, Expression expression)
{
if (_size >= _params.length) {
expandCapacity();
}
Parameter param = new Parameter();
param.type = type;
param.name = name;
param.value = value;
param.expression = expression;
param.required = (value == null);
_params[_size++] = param;
}
public ParameterListDeclaration open()
{
add(CompilableFunction.PARAMETER_CONTEXT, "<context>", null, null);
return this;
}
public ParameterListDeclaration close()
{
int n = _size;
int c = 0;
Parameter[] params = _params;
out:
for(int i=0; i<n; i++) {
switch(params[i].type) {
case CompilableFunction.PARAMETER_CONTEXT:
case CompilableFunction.PARAMETER_ARRAY:
case CompilableFunction.PARAMETER_ANYLIST:
case CompilableFunction.PARAMETER_LIST:
case CompilableFunction.PARAMETER_REST:
break;
default:
{
if (params[i].required) {
c++;
} else {
break out;
}
}
break;
}
}
_minsize = c;
return this;
}
public void declareTo(FunctionStatement function)
{
int n = _size;
Parameter[] params = _params;
for(int i=0; i<n; i++) {
Parameter param = params[i];
int type = param.type;
if (type == CompilableFunction.PARAMETER_ANY || type == CompilableFunction.PARAMETER_REST) {
function.declareParameter(param.name);
}
}
}
public void check(ErrorListener context)
{
Parameter[] params = _params;
int n = _size;
for(int i=0; i<n; i++) {
Parameter param = params[i];
Expression expr = param.expression;
if (expr != null) {
expr.check(context);
if (expr.isConstant()) {
param.value = expr.eval();
} else {
context.error(expr.getLocation(), "Default value for parameter '"+param.name+"' is not a constant");
}
}
}
}
public void importDocuments(Doc node)
{
if (node != null) {
Parameter[] params = _params;
int length = _size;
for(int i=0; i<length; i++) {
Parameter param = params[i];
if (param.type != CompilableFunction.PARAMETER_CONTEXT) {
param.doc = node.findFirst(Doc.T_PARAM, param.name);
}
}
}
}
public void compileDescriptor(ByteCompiler context)
{
ClassRoom clazz = context.getClassRoom();
ConstantPool pool = context.getPool();
Parameter[] params = _params;
int size = _size;
int count = size;
for(int i=0; i<size; i++) {
if (params[i].value != null) {
count++;
}
}
Code code = clazz.getStatic().getCode();
code.iconst(count);
code.anewarray("java/lang/Object");
int j = 0;
for(int i=0; i<size; i++) {
Parameter param = params[i];
Any defaultvalue = param.value;
code.dup();
code.iconst(j++);
if (param.type == CompilableFunction.PARAMETER_REST) {
code.astring(".."+param.name);
} else if (defaultvalue != null) {
code.astring("*"+param.name);
} else {
code.astring(param.name);
}
code.aastore();
if (defaultvalue != null) {
code.dup();
code.iconst(j++);
context.constant(defaultvalue, false);
code.aastore();
}
}
}
}