com.nokia.xfolite.xforms.xpath.XFormsCoreFunctionLibrary.java Source code

Java tutorial

Introduction

Here is the source code for com.nokia.xfolite.xforms.xpath.XFormsCoreFunctionLibrary.java

Source

/*
 * This file is part of: Xfolite (J2ME XForms client)
 *
 * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
 *
 * Contact: Oskari Koskimies <oskari.koskimies@nokia.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser
 * General Public License along with this program.
 * If not, see <http://www.gnu.org/licenses/>.
 */

package com.nokia.xfolite.xforms.xpath;

import java.util.Hashtable;
import java.util.Vector;
import java.util.Calendar;
import javax.microedition.midlet.MIDlet;

import com.nokia.xfolite.xforms.dom.UserInterface;
import com.nokia.xfolite.xforms.model.datatypes.DataTypeDate;
import com.nokia.xfolite.xforms.model.datatypes.DataTypeDuration;
import com.nokia.xfolite.xml.dom.Document;
import com.nokia.xfolite.xml.dom.Element;
import com.nokia.xfolite.xml.dom.Node;
import com.nokia.xfolite.xml.xpath.ExpandedNameNode;
import com.nokia.xfolite.xml.xpath.NodeSet;
import com.nokia.xfolite.xml.xpath.XPathContext;
import com.nokia.xfolite.xml.xpath.XPathCoreFunctionLibrary;
import com.nokia.xfolite.xml.xpath.XPathException;
import com.nokia.xfolite.xml.xpath.XPathFunctionLibrary;
import com.nokia.xfolite.xml.xpath.XPathLexer;

import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.*;
import org.bouncycastle.util.encoders.*;
import java.io.*;

public class XFormsCoreFunctionLibrary implements XPathFunctionLibrary {

    private static final String[] XFORMS_CORE_FUNCTIONS = { "avg" //0
            , "boolean-from-string" //1
            , "count-non-empty" //2
            , "current" //3
            , "days-from-date" //4
            , "digest" //5
            , "exists" //6  - ORBEON EXTENSION
            , "if" //7
            , "index" //8
            , "instance" //9
            , "max" //10
            , "min" //11
            , "month-name" //12 - NOKIA EXTENSION
            , "months" //13
            , "now" //14
            , "property" //15
            , "seconds" //16
            , "seconds-from-dateTime" //17
            , "weekday-name" //18 - NOKIA EXTENSION
    };

    private static final byte[][] XFORMS_FUNCTIONS_ARGUMENT_COUNT = { { 1, 1 } //0      avg
            , { 1, 1 } //1      boolean-from-string
            , { 1, 1 } //2      count-non-empty
            , { 0, 0 } //3      current
            , { 1, 1 } //4      days-from-date
            , { 2, 3 } //4      digest
            , { 1, 1 } //5       exists
            , { 3, 3 } //6      if
            , { 1, 1 } //7      index
            , { 1, 1 } //8      instance
            , { 1, 1 } //9      max
            , { 1, 1 } //10   min
            , { 2, 2 } //11   month-name
            , { 1, 1 } //12   months
            , { 0, 0 } //13   now
            , { 1, 1 } //14   property
            , { 1, 1 } //15   seconds
            , { 1, 1 } //16   seconds-from-dateTime
            , { 2, 2 } //17   weekday-name

    };

    private Hashtable m_instanceMap = null;
    private Hashtable m_indexMap = null;
    private UserInterface m_ui = null;

    public void setUI(UserInterface ui) {
        m_ui = ui;
    }

    public void setInstanceMap(Hashtable instanceMap) {
        m_instanceMap = instanceMap;
    }

    public void addInstance(String instanceId, Document instanceDoc) {
        if (m_instanceMap == null)
            m_instanceMap = new Hashtable();
        m_instanceMap.put(instanceId, instanceDoc);
    }

    public void removeInstance(String instanceId) {
        if (m_instanceMap != null)
            m_instanceMap.remove(instanceId);
    }

    public int RecognizeFunction(ExpandedNameNode functionName, int arity) {
        //TODO: namespace uri is ignored here on purpose
        int index = XPathLexer.BinarySearch(XFORMS_CORE_FUNCTIONS, functionName.getLocalName());
        if (index >= 0 && arity >= XFORMS_FUNCTIONS_ARGUMENT_COUNT[index][0]
                && arity <= XFORMS_FUNCTIONS_ARGUMENT_COUNT[index][1]) //function found
        {
            return index;
        }
        return -1;
    }

    public Object EvaluateIndexedFunction(int index, XPathContext focus, Vector args, Node originalContext)
            throws XPathException {

        if (index >= XFORMS_CORE_FUNCTIONS.length || index < 0)
            throw new XPathException(XPathException.TYPE_ERR, "index for Xforms core function out of bound");

        int args_count = args.size() - 1;
        if (args_count < XFORMS_FUNCTIONS_ARGUMENT_COUNT[index][0]
                || args_count > XFORMS_FUNCTIONS_ARGUMENT_COUNT[index][1])
            throw new XPathException(XPathException.TYPE_ERR, "illegal number of arguments for xforms function");

        switch (index) {
        case 0: //   avg
            if (args.elementAt(1) instanceof NodeSet) {
                return avg_function((NodeSet) args.elementAt(1));
            } else
                throw new XPathException(XPathException.TYPE_ERR,
                        "xforms function avg() expects nodeset as an argument");
        case 1: //   boolean-from-string
            return booleanfromstring_function(XPathCoreFunctionLibrary.string_function(args.elementAt(1)));
        case 2: //   count-non-empty
            if (args.elementAt(1) instanceof NodeSet) {
                return countnonempty_function((NodeSet) args.elementAt(1));
            } else
                throw new XPathException(XPathException.TYPE_ERR,
                        "xforms function count-non-empty() expects nodeset as an argument");

        case 3: //   current
            return new NodeSet(originalContext);
        case 4: //   days-from-date
            return daysfromdate_function(XPathCoreFunctionLibrary.string_function(args.elementAt(1)));
        case 5: // digest
            String encoding = "base64";
            if (args_count == 3) {
                encoding = XPathCoreFunctionLibrary.string_function(args.elementAt(3));
            }
            return digest_function(XPathCoreFunctionLibrary.string_function(args.elementAt(1)),
                    XPathCoreFunctionLibrary.string_function(args.elementAt(2)), encoding);

        case 6: // exists
            if (args.elementAt(1) instanceof NodeSet) {
                return exists_function((NodeSet) args.elementAt(1));
            } else
                throw new XPathException(XPathException.TYPE_ERR,
                        "xforms function exists() expects nodeset as an argument");
        case 7: //   if
            return if_function(XPathCoreFunctionLibrary.boolean_function(args.elementAt(1)),
                    XPathCoreFunctionLibrary.string_function(args.elementAt(2)),
                    XPathCoreFunctionLibrary.string_function(args.elementAt(3)));
        case 8: //   index
            return index_function(XPathCoreFunctionLibrary.string_function(args.elementAt(1)));
        case 9: //   instance
            return instance_function(XPathCoreFunctionLibrary.string_function(args.elementAt(1)));
        case 10: //   max
            if (args.elementAt(1) instanceof NodeSet) {
                return max_function((NodeSet) args.elementAt(1));
            } else
                throw new XPathException(XPathException.TYPE_ERR,
                        "xforms function max() expects nodeset as an argument");

        case 11: //   min
            if (args.elementAt(1) instanceof NodeSet) {
                return min_function((NodeSet) args.elementAt(1));
            } else
                throw new XPathException(XPathException.TYPE_ERR,
                        "xforms function min() expects nodeset as an argument");

        case 12: //   month-name
            if (args.elementAt(2) instanceof NodeSet) {
                return month_name_function(XPathCoreFunctionLibrary.string_function(args.elementAt(1)),
                        (NodeSet) args.elementAt(2));
            } else
                throw new XPathException(XPathException.TYPE_ERR,
                        "xforms function month-name() expects nodeset as a second argument");

        case 13: //   months
            return months_function(XPathCoreFunctionLibrary.string_function(args.elementAt(1)));

        case 14: //   now
            return now_function();
        case 15: //   property
            return property_function(XPathCoreFunctionLibrary.string_function(args.elementAt(1)), m_ui);
        case 16: //   seconds
            return seconds_function(XPathCoreFunctionLibrary.string_function(args.elementAt(1)));
        case 17: //   seconds-from-dateTime
            return secondsfromdateTime_function(XPathCoreFunctionLibrary.string_function(args.elementAt(1)));
        case 18: //   weekday-name
            if (args.elementAt(2) instanceof NodeSet) {
                return weekday_name_function(XPathCoreFunctionLibrary.string_function(args.elementAt(1)),
                        (NodeSet) args.elementAt(2));
            } else
                throw new XPathException(XPathException.TYPE_ERR,
                        "xforms function weekday-name() expects nodeset as a second argument");
        }

        return null;
    }

    public static Double avg_function(NodeSet ns) {
        double sum = XPathCoreFunctionLibrary.sum_function(ns).doubleValue();
        double count = ns.getLength();
        return new Double(sum / count);
    }

    public static Boolean booleanfromstring_function(String s) {
        String lc_string = s.toLowerCase();
        boolean result = false;
        if (lc_string.equals("1") || lc_string.equals("true"))
            result = true;
        return new Boolean(result);

    }

    public static Double countnonempty_function(NodeSet ns) {
        int ns_count = ns.getLength();
        int count = 0;
        for (int i = 0; i < ns_count; i++) {
            if (XPathCoreFunctionLibrary.string_function(ns.item(i)).length() > 0)
                count++;
        }
        return new Double(count);
    }

    public static Boolean exists_function(NodeSet ns) {
        return (ns.getLength() > 0) ? Boolean.TRUE : Boolean.FALSE;
    }

    public static Double daysfromdate_function(String s) {
        // TODO: Check that this returns right value
        // It should work fine when you want to get the day difference between two dates, but might be wrong 
        // if you want to look at the value for one date.
        Calendar cal = DataTypeDate.xsdDateTime2Calendar(s);
        return new Double(cal.getTime().getTime() / 1000 / 3600 / 24);
    }

    public static String digest_function(String s, String method, String encoding) {
        //#debug info
        System.out.println("Digest function invoked with params(" + s + "," + method + "," + encoding + ")");
        Digest digest = null;
        Encoder encoder = null;
        if (encoding.equals("base64")) {
            encoder = new Base64Encoder();
        } else if (encoding.equals("hex")) {
            encoder = new HexEncoder();
        }
        if (method.equals("SHA-1")) {
            digest = new SHA1Digest();
        } else if (method.equals("SHA-256")) {
            digest = new SHA256Digest();
        } else if (method.equals("SHA-512")) {
            digest = new SHA512Digest();
        } else if (method.equals("MD5")) {
            digest = new MD5Digest();
        }

        if (encoder == null) {
            throw new XPathException(XPathException.TYPE_ERR,
                    "XForms function digest() only supports hex and base64 encoding.");
        }
        if (digest == null) {
            throw new XPathException(XPathException.TYPE_ERR,
                    "XForms function digest() only supports MD5, SHA-1, SHA-256 and SHA-512 digests.");
        }

        int len = s.length();
        for (int i = 0; i < len; i++) {
            digest.update((byte) s.charAt(i)); // FIXME: Better not use non-ASCII characters!
        }

        byte[] data = new byte[digest.getDigestSize()];
        digest.doFinal(data, 0);

        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        try {
            encoder.encode(data, 0, data.length, bOut);
        } catch (IOException e) {
            throw new XPathException(XPathException.TYPE_ERR, "Exception when encoding digest: " + e);
        }

        byte[] out = bOut.toByteArray();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < out.length; i++) {
            sb.append((char) out[i]); // This works fine, neither hex nor base64 encodings produce 
        }
        return sb.toString();
    }

    public static String if_function(Boolean TestCase, String Ifpart, String ElsePart) {
        if (TestCase.booleanValue())
            return Ifpart;
        else
            return ElsePart;
    }

    private Double index_function(String s) {
        if (m_indexMap == null) {
            return new Double(Double.NaN);
        }
        Double value = (Double) m_indexMap.get(s);
        if (value == null) {
            return new Double(Double.NaN);
        }
        return value;
    }

    /*
     * Set the value returned by index function.
     * Note that values <= 0 will result in a NaN value being stored.
     */
    public void setIndex(String id, int value) {
        if (m_indexMap == null) {
            m_indexMap = new Hashtable();
        }
        if (value > 0) {
            m_indexMap.put(id, new Double(value));
        } else {
            m_indexMap.put(id, new Double(Double.NaN));
        }
    }

    public static Double max_function(NodeSet ns) {
        double max = Double.NaN;
        int ns_length = ns.getLength();
        for (int i = 0; i < ns_length; i++) {
            Double D = XPathCoreFunctionLibrary
                    .number_function(XPathCoreFunctionLibrary.string_function(ns.item(i)));
            if (D.isNaN())
                return D;

            if (max == Double.NaN || D.doubleValue() > max)
                max = D.doubleValue();
        }
        return new Double(max);
    }

    public static Double min_function(NodeSet ns) {
        double min = Double.NaN;
        int ns_length = ns.getLength();
        for (int i = 0; i < ns_length; i++) {
            Double D = XPathCoreFunctionLibrary
                    .number_function(XPathCoreFunctionLibrary.string_function(ns.item(i)));
            if (D.isNaN())
                return D;
            if (min == Double.NaN || D.doubleValue() < min)
                min = D.doubleValue();
        }
        return new Double(min);
    }

    public static String month_name_function(String s, NodeSet ns) {
        //#debug info
        System.out.println("****************** Invoking month_name_function with parameter <" + s + ">");

        try {
            String[] months = new String[DataTypeDate.MONTH_NAMES.length];
            int len = ns.getLength();
            for (int i = 0; i < len; i++) {
                months[i] = XPathCoreFunctionLibrary.string_function(ns.item(i));
            }
            for (int i = len; i < DataTypeDate.MONTH_NAMES.length; i++) {
                months[i] = DataTypeDate.MONTH_NAMES[i];
            }

            Calendar cal = DataTypeDate.xsdDate2Calendar(s);
            return DataTypeDate.getMonthName(cal, months);
        } catch (Exception ex) {
            return "";
        }
    }

    public static Double months_function(String s) {
        return new Double(DataTypeDuration.xsdDuration2seconds(s) / (30 * 24 * 60 * 60));
    }

    public static String now_function() {
        Calendar cal = Calendar.getInstance();
        return DataTypeDate.calendar2xsdDateTime(cal);
    }

    public static String property_function(String s, UserInterface ui) {
        String property = null;
        if (s.equals("version"))
            property = "1.0";
        else if (s.equals("conformance-level"))
            property = "basic";
        else if (ui != null) {
            property = ui.getProperty(s);
            //#debug info
            System.out.println("Got property for " + s + ": " + property);
        }
        return property == null ? "" : property;
    }

    public static Double seconds_function(String s) {
        return new Double(DataTypeDuration.xsdDuration2seconds(s));
    }

    public static Double secondsfromdateTime_function(String s) {
        Calendar cal = DataTypeDate.xsdDateTime2Calendar(s);
        return new Double(cal.getTime().getTime() / 1000);
    }

    public NodeSet instance_function(String id) {
        if (m_instanceMap != null && m_instanceMap.containsKey(id)) {
            Element rootelement = ((Document) m_instanceMap.get(id)).getDocumentElement();
            if (rootelement != null)
                return new NodeSet(rootelement);
        }
        return new NodeSet();
    }

    public static String weekday_name_function(String s, NodeSet ns) {
        try {
            String[] weekdays = new String[DataTypeDate.WEEKDAY_NAMES.length];
            int len = ns.getLength();
            for (int i = 0; i < len; i++) {
                weekdays[i] = XPathCoreFunctionLibrary.string_function(ns.item(i));
            }
            for (int i = len; i < DataTypeDate.WEEKDAY_NAMES.length; i++) {
                weekdays[i] = DataTypeDate.WEEKDAY_NAMES[i];
            }

            Calendar cal = DataTypeDate.xsdDate2Calendar(s);
            return DataTypeDate.getWeekDayName(cal, weekdays);
        } catch (Exception ex) {
            return "";
        }
    }
}