/*
* © Copyright IBM Corp. 2010
*
* 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.ibm.xsp.extlib.sbt.javascript;
import javax.faces.context.FacesContext;
import javax.swing.plaf.basic.BasicProgressBarUI;
import com.ibm.commons.runtime.Context;
import com.ibm.commons.util.StringUtil;
import com.ibm.jscript.InterpretException;
import com.ibm.jscript.JSContext;
import com.ibm.jscript.JavaScriptException;
import com.ibm.jscript.engine.IExecutionContext;
import com.ibm.jscript.types.BuiltinFunction;
import com.ibm.jscript.types.FBSDefaultObject;
import com.ibm.jscript.types.FBSGlobalObject;
import com.ibm.jscript.types.FBSObject;
import com.ibm.jscript.types.FBSUtility;
import com.ibm.jscript.types.FBSValue;
import com.ibm.jscript.types.FBSValueVector;
import com.ibm.sbt.service.basic.ProxyEndpointService;
import com.ibm.sbt.service.basic.ProxyService;
import com.ibm.sbt.services.endpoints.EndpointFactory;
import com.ibm.xsp.extlib.social.PeopleService;
import com.ibm.xsp.extlib.social.SocialServicesFactory;
/**
* Extended Notes/Domino formula language.
* <p>
* This class implements a set of new functions available to the JavaScript interpreter. They become available to Domino
* Designer in the category "@NotesFunctionEx".
* </p>
*/
public class SBTFunctions extends FBSDefaultObject {
// Functions IDs
private static final int FCT_ENDPOINT = 1;
private static final int FCT_IDENTITYFROMID = 3;
private static final int FCT_IDFROMIDENTITY = 4;
private static final int FCT_PROXYURL = 5;
// ============================= CODE COMPLETION ==========================
//
// Even though JavaScript is an untyped language, the XPages JavaScript
// interpreter can make use of symbolic information defining the
// objects/functions exposed. This is particularly used by Domino Designer
// to provide the code completion facility and help the user writing code.
//
// Each function expose by a library can then have one or multiple
// "prototypes", defining its parameters and the returned value type. To
// make this definition as efficient as possible, the parameter definition
// is compacted within a string, where all the parameters are defined
// within parenthesis followed by the returned value type.
// A parameter is defined by its name, followed by a colon and its type.
// Generally, the type is defined by a single character (see bellow) or a
// full Java class name. The returned type is defined right after the
// closing parameter parenthesis.
//
// Here is, for example, the definition of the "@Date" function which can
// take 3 different set of parameters:
// "(time:Y):Y",
// "(years:Imonths:Idays:I):Y",
// "(years:Imonths:Idays:Ihours:Iminutes:Iseconds:I):Y");
//
// List of types
// V void
// C char
// B byte
// S short
// I int
// J long
// F float
// D double
// Z boolean
// T string
// Y date/time
// W any (variant)
// N multiple (...)
// L<name>; object
// ex:
// (entries:[Lcom.ibm.xsp.extlib.MyClass;):V
//
// =========================================================================
public SBTFunctions(JSContext jsContext) {
super(jsContext, null, false);
// Document helpers
addFunction(FCT_ENDPOINT, "@Endpoint", "(name:T):Lcom.ibm.xsp.extlib.sbt.services.client.Endpoint;"); // $NON-NLS-1$ $NON-NLS-2$
addFunction(FCT_IDENTITYFROMID, "@IdentityFromId", "(target:Tid:T):T"); // $NON-NLS-1$ $NON-NLS-2$
addFunction(FCT_IDFROMIDENTITY, "@IdFromIdentity", "(target:Tidentity:T):T"); // $NON-NLS-1$ $NON-NLS-2$
addFunction(FCT_PROXYURL, "@ProxyUrl", "(proxyName:Tendpoint:Turl:T):T"); // $NON-NLS-1$ $NON-NLS-2$
}
private void addFunction(int index, String functionName, String... params) {
createMethod(functionName, FBSObject.P_NODELETE | FBSObject.P_READONLY, new NotesFunction(getJSContext(),
index, functionName, params));
}
@Override
public boolean hasInstance(FBSValue v) {
return v instanceof FBSGlobalObject;
}
@Override
public boolean isJavaNative() {
return false;
}
// =================================================================================
// Functions implementation
// For optimization reasons, there is one NotesFunction instance per function,
// instead of one class (this avoids loading to many classes). To then distinguish
// the actual function, it uses an index member.
// =================================================================================
public static class NotesFunction extends BuiltinFunction {
private String functionName;
private int index;
private String[] params;
NotesFunction(JSContext jsContext, int index, String functionName, String[] params) {
super(jsContext);
this.functionName = functionName;
this.index = index;
this.params = params;
}
/**
* Index of the function.
* <p>
* There must be one instanceof this class per index.
* </p>
*/
public int getIndex() {
return this.index;
}
/**
* Return the list of the function parameters.
* <p>
* Note that this list is not used at runtime, at least for now, but consumed by Designer code completion.<br>
* A function can expose multiple parameter sets.
* </p>
*/
@Override
protected String[] getCallParameters() {
return this.params;
}
/**
* Function name, as exposed by Designer and use at runtime.
* <p>
* This function is exposed in the JavaScript global namespace, so you should be careful to avoid any name
* conflict.
* </p>
*/
@Override
public String getFunctionName() {
return this.functionName;
}
/**
* Actual code execution.
* <p>
* The JS runtime calls this method when the method is executed within a JavaScript formula.
* </p>
*
* @param context
* the JavaScript execution context (global variables, function...)
* @param args
* the arguments passed to the function
* @param _this the "this" object when the method is called as a "this" member
*/
@Override
public FBSValue call(IExecutionContext context, FBSValueVector args, FBSObject _this)
throws JavaScriptException {
try {
// Else execute the formulas
switch (index) {
// ////////////////////////////////////////////////////////////////////////////////////////
// Document IDs
// ////////////////////////////////////////////////////////////////////////////////////////
case FCT_ENDPOINT: {
if (args.size() >= 1) {
int type = 0;
if (args.size() >= 2) {
type = args.get(1).intValue();
}
String name = args.get(0).stringValue();
switch(type) {
case 0:
return FBSUtility.wrap(context.getJSContext(),EndpointFactory.getEndpoint(name));
case 1:
return FBSUtility.wrap(context.getJSContext(),EndpointFactory.getEndpointName(name));
case 2:
return FBSUtility.wrap(context.getJSContext(),EndpointFactory.getEndpointLabel(name));
}
}
}
break;
case FCT_IDENTITYFROMID: {
if (args.size() >= 2) {
String target = args.get(0).stringValue();
String id = args.get(1).stringValue();
PeopleService svc = SocialServicesFactory.getInstance().getPeopleService();
return FBSUtility.wrap(context.getJSContext(),svc.getUserIdentityFromId(target, id));
}
}
break;
case FCT_IDFROMIDENTITY: {
if (args.size() >= 2) {
String target = args.get(0).stringValue();
String id = args.get(1).stringValue();
PeopleService svc = SocialServicesFactory.getInstance().getPeopleService();
return FBSUtility.wrap(context.getJSContext(),svc.getUserIdFromIdentity(target, id));
}
}
break;
case FCT_PROXYURL: {
if (args.size() >= 2) {
String proxyName = args.get(0).stringValue();
String endpoint = args.get(1).stringValue();
String url = null;
if (args.size() >= 3) {
url = args.get(2).stringValue();
}
//TODO Padraic - shoudl this be facesContext?
Context ctx = Context.getUnchecked();
// String proxiedUrl = ProxyEndpointService.getProxyUrlForEndpoint(FacesContext.getCurrentInstance(), proxyName, endpoint, url);
String proxiedUrl = ProxyEndpointService.getProxyUrlForEndpoint(ctx, proxyName, endpoint, url);
return FBSUtility.wrap(proxiedUrl);
}
}
break;
default: {
throw new InterpretException(null, StringUtil.format(
"Internal error: unknown function \'{0}\'", functionName));
}
}
// } catch (InterpretException e) {
// throw e;
// } catch (NotesException e) {
// // This case covers where a call to session.evaluate() throws a NotesException
// // We want to continue rendering the page but allow @IsError to pick up on this issue
// // so we return @Error (NaN / FBSUndefined.undefinedValue)
// return FBSUndefined.undefinedValue;
} catch (Exception e) {
throw new InterpretException(e, StringUtil.format("Error while executing function \'{0}\'",
functionName));
}
throw new InterpretException(null, StringUtil.format("Cannot evaluate function \'{0}\'", functionName));
}
}
}