All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jpedal.objects.javascript.functions.JSFunction Maven / Gradle / Ivy

There is a newer version: 7.15.25
Show newest version
/*
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.idrsolutions.com
 * Help section for developers at http://www.idrsolutions.com/support/
 *
 * (C) Copyright 1997-2017 IDRsolutions and Contributors.
 *
 * This file is part of JPedal/JPDF2HTML5
 *
 @LICENSE@
 *
 * ---------------
 * JSFunction.java
 * ---------------
 */
package org.jpedal.objects.javascript.functions;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JOptionPane;

import org.jpedal.constants.ErrorCodes;
import org.jpedal.objects.acroforms.AcroRenderer;
import org.jpedal.objects.acroforms.ReturnValues;
import org.jpedal.objects.raw.FormObject;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.parser.DecoderOptions;
import org.jpedal.utils.LogWriter;
import org.jpedal.utils.StringUtils;

/**
 * base class for functions with shared code
 */

public class JSFunction {

    final AcroRenderer acro;
    final FormObject formObject;

    public static final int AFDate = 1;
    public static final int AFNumber = 2;
    public static final int AFPercent = 3;
    public static final int AFRange = 4;
    public static final int AFSimple = 5;
    public static final int AFSpecial = 6;
    public static final int AFTime = 7;

    static final int AVG = 1;
    static final int SUM = 2;
    static final int PRD = 3;
    static final int MIN = 4;
    static final int MAX = 5;

    public static final int UNKNOWN = -1;
    public static final int KEYSTROKE = 1;
    public static final int VALIDATE = 2;
    public static final int FORMAT = 3;
    public static final int CALCULATE = 4;

    public boolean DECIMAL_IS_COMMA;

    String value;

    private static int staticGapformat = -1;
    private static int staticDecimalcount = -1;

    /**
     * sets generic values for the gap format and the decimal count
*
* int gapFormat:
* 0 Comma separated, period decimal point
* 1 No separator, period decimal point
* 2 Period separated, comma decimal point
* 3 No separator, comma decimal point
*
* int decCount - the number of decimal places after the decimal point. *

* if either is -1 it is ignored */ public static void setValidDataFormat(final int gapFormat, final int decCount) { staticDecimalcount = decCount; staticGapformat = gapFormat; } public static int getStaticGapFormat() { return staticGapformat; } public static int getStaticDecimalCount() { return staticDecimalcount; } public JSFunction(final AcroRenderer acro, final FormObject formObject) { this.acro = acro; this.formObject = formObject; } public static void debug(final String str) { if (LogWriter.isRunningFromIDE) { System.out.println("[javascript] " + str); org.jpedal.objects.acroforms.utils.ConvertToString.printStackTrace(2); if (str.startsWith("Unknown") && 1 == 1) { throw new RuntimeException("Exception"); } } } /** * apply one of more matching patterns and return where a match */ protected static String applyRegexp(final String text, final String[] patterns) { String matchedString = ""; final int patternCount = patterns.length; for (int i = 0; i < patternCount; i++) { final Pattern pa = Pattern.compile(patterns[i]); final Matcher m = pa.matcher(text); if (m.matches()) { i = patternCount; final int start = m.start(); final int end = m.end(); matchedString = text.substring(start, end); //System.out.println(matchedString+" "+patterns[i]); } } return matchedString; } /** * general routine to handle array values */ String processArray(final String nextValue, final int operation) { float result = 0; boolean resultNotSet = true; boolean hasDec = false, hasData = false; final String[] args2 = convertToArray(nextValue); //add together except first item (which is new Array) final float arrayCount = args2.length; for (int ii = 1; ii < arrayCount; ii++) { //nextValue=stripQuotes(args2[ii]); // String val=(String)formObject.getFormValue(); String val = null; final String strippedValue = args2[ii].replaceAll("\"", ""); final Object[] os = acro.getFormComponents(strippedValue, ReturnValues.FORMOBJECTS_FROM_NAME, -1); // System.out.println(os.length); if (os.length > 0) { final FormObject o = (FormObject) os[0]; val = o.getValue(); } //if string is empty set value to 0 in calculations if (val == null || val.isEmpty()) { val = "0"; } hasData = true; final boolean isNegative = val.startsWith("-"); final float nextVal; //interprete the value properly, replace commas or remove full stops. if (DECIMAL_IS_COMMA) { val = val.replaceAll("\\.", ""); // "\\. as its regular expression and quotes next char ie '\.' is decimal val = val.replaceAll(",", "."); } else { //check if we only have a comma if (val.indexOf(',') != -1 && !val.contains(".")) { val = val.replace(',', '.'); } else { val = val.replaceAll(",", ""); } } //flag if any values and if decimal if (val.indexOf('.') != -1) { hasDec = true; } if (isNegative) { nextVal = -Float.parseFloat(val.substring(1)); } else { nextVal = Float.parseFloat(val); } switch (operation) { case AVG: result += nextVal; break; case SUM: result += nextVal; break; case PRD: if (resultNotSet) { result = 1; resultNotSet = false; } result *= nextVal; break; case MIN: if (ii == 1) { result = nextVal; } else if (nextVal < result) { result = nextVal; } break; case MAX: if (ii == 1) { result = nextVal; } else if (nextVal > result) { result = nextVal; } break; default: debug("Unsupported op " + operation + " in processArray"); break; } } //post process if (operation == AVG) { result /= (arrayCount - 1); } if (hasDec) { return String.valueOf(result); } else if (!hasData) { return ""; } else { return String.valueOf((int) result); } } /** * turn javascript string into values in an array */ public static String[] convertToArray(String js) { final String rawCommand = js; final int ptr = js.indexOf('('); int items = 0, count = 0; final String[] values; String finalValue = ""; final List rawValues = new ArrayList(); /* * first value is command */ if (ptr != -1) { final String com = js.substring(0, ptr); rawValues.add(com); items++; //remove js = js.substring(ptr, js.length()).trim(); int charsAtEnd = 1; //lose ; as well if (js.endsWith(";")) { charsAtEnd++; } //remove main brackets and possibly ; if (js.startsWith("(")) //strip brackets { js = js.substring(1, js.length() - charsAtEnd); } else { debug("Unknown args in " + rawCommand); } } /* * break into values allowing for nested values */ final StringTokenizer tokens = new StringTokenizer(js, "(,);", true); while (tokens.hasMoreTokens()) { //get value final StringBuilder nextValueStr = new StringBuilder(tokens.nextToken()); //allow for comma in brackets while (tokens.hasMoreTokens() && nextValueStr.toString().startsWith("\"") && !nextValueStr.toString().endsWith("\"")) { nextValueStr.append(tokens.nextToken()); } final String nextValue = nextValueStr.toString(); if (count == 0 && nextValue.equals(",")) { rawValues.add(finalValue); finalValue = ""; items++; } else { if (nextValue.equals("(")) { count++; } else if (nextValue.equals(")")) { count--; } finalValue += nextValue; } } //last value items++; rawValues.add(finalValue); //turn into String array to avoid casting later //(could be rewritten later to be cleaner if time/performance issue) values = new String[items]; for (int ii = 0; ii < items; ii++) { values[ii] = (rawValues.get(ii)).trim(); //System.out.println(ii+" >"+values[ii]+"<"); } return values; } /** * ensure any empty slots at start filled */ private static String padString(final String rawVal, final int maxLen) { final int length = rawVal.length(); if (maxLen == length) { return rawVal; } else if (maxLen < length) { return rawVal; } else { final StringBuilder paddedString = new StringBuilder(); final int extraChars = maxLen - length; for (int jj = 0; jj < extraChars; jj++) { paddedString.append('0'); } paddedString.append(rawVal); return paddedString.toString(); } } //alert user and reset to old value void maskAlert(final int code, final Object[] args) { //restore old value String validValue = formObject.getLastValidValue(); if (validValue == null) { validValue = ""; } formObject.setLastValidValue(validValue); //may not be needed as is same value as should be there formObject.updateValue(validValue, false, true); if (((String) args[0]).contains(" R")) { args[0] = formObject.getTextStreamValue(PdfDictionary.T); } reportError(code, args); } //apply formatting in mask to data //returns null if invalid String validateMask(final String[] args, final String separator, final boolean useDefaultValues) { final String[] months = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; final int[] monthsCount = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int monthMod = 1; int monthValue = 0; int dayValue = 0; String validValue = null; final int count = args.length; if (count != 2) { String list = ""; for (int i = 0; i < count; i++) { if (i == 0) { list = args[i]; } else { list = list + ',' + args[i]; } } JSFunction.debug("Unexpected values items=" + count + '{' + list + '}'); } else { boolean isValid = true; //assume okay and disprove String formData; formData = (String) formObject.getFormValue(); if (formData == null || formData.isEmpty()) { return ""; } final String endText; //some values have additions such as PM/AM final int space = formData.lastIndexOf(' '); if (space != -1) { endText = formData.substring(space + 1).toLowerCase().trim(); //must end am or pm is does, strip it off if ((endText != null) && (endText.equals("am") || endText.equals("pm"))) { formData = formData.substring(0, space); // else // return null; } } String mask = stripQuotes(args[1]); //Day must be "XX" not "X" final int d = mask.indexOf('d'); if (mask.charAt(d + 1) != 'd') { mask = mask.replaceFirst("d", "dd"); } final StringTokenizer maskValues = new StringTokenizer(mask, separator, true); //ie mm:dd:yyyy final StringTokenizer formValues = new StringTokenizer(formData, separator, true); //ie 01:01:2007 //match each part final StringBuilder finalValue = new StringBuilder(); String nextMask, nextVal, nextSep, paddedValue; //get a time instance and defaults for all Date values here final GregorianCalendar gc = new GregorianCalendar(); //loop through and test each value while (maskValues.hasMoreTokens()) { paddedValue = ""; //get next mask and any next value (allowing for multiple separators) while (true) { nextMask = maskValues.nextToken(); if (!separator.contains(nextMask) || !maskValues.hasMoreTokens()) { break; } //its muliple separators so append and retry finalValue.append(nextMask); } while (true) { //get form if there is one if (!formValues.hasMoreTokens()) { nextVal = null; //run out of values } else { nextVal = formValues.nextToken(); } if (nextVal == null || !separator.contains(nextVal) || !formValues.hasMoreTokens()) { break; } } if (maskValues.hasMoreTokens()) { nextSep = maskValues.nextToken(); } else { nextSep = null; } if (nextVal != null) { paddedValue = padString(nextVal, nextMask.length()); } if (nextMask.equals("h")) { //12 hour clock //allow for null value in Date if (useDefaultValues && nextVal == null) { paddedValue = String.valueOf(gc.get(Calendar.HOUR)); /* added to make the testing be more predictable */ if (org.jpedal.objects.javascript.defaultactions.JpedalDefaultJavascript.testingSetStaticDate) { paddedValue = "10"; //new Date(110,10,10,10,10,10); //year,month,date,hrs,mins,secs } } else { paddedValue = padString(nextVal, 2); } isValid = verifyNumberInRange(paddedValue, 0, 11); } else if (nextMask.equals("HH")) { //24 hours clock //allow for null value in Date if (useDefaultValues && nextVal == null) { paddedValue = String.valueOf(gc.get(Calendar.HOUR_OF_DAY)); /* added to make the testing be more predictable */ if (org.jpedal.objects.javascript.defaultactions.JpedalDefaultJavascript.testingSetStaticDate) { paddedValue = "10"; //new Date(110,10,10,10,10,10); //year,month,date,hrs,mins,secs } paddedValue = padString(paddedValue, 2); isValid = verifyNumberInRange(paddedValue, 0, 23); } else { isValid = verifyNumberInRange(paddedValue, 0, 23); } } else if (nextMask.equals("MM")) { //allow for null value in Date if (useDefaultValues && nextVal == null) { paddedValue = String.valueOf(gc.get(Calendar.MINUTE)); /* added to make the testing be more predictable */ if (org.jpedal.objects.javascript.defaultactions.JpedalDefaultJavascript.testingSetStaticDate) { paddedValue = "10"; //new Date(110,10,10,10,10,10); //year,month,date,hrs,mins,secs } paddedValue = padString(paddedValue, 2); isValid = verifyNumberInRange(paddedValue, 0, 59); } else { isValid = verifyNumberInRange(paddedValue, 0, 59); } } else if (nextMask.equals("mm") || nextMask.equals("m")) { isValid = verifyNumberInRange(paddedValue, 0, 12); if (isValid) { final int numVal = Integer.parseInt(paddedValue); if ((paddedValue.length() != nextMask.length()) && (nextMask.length() == 1)) { paddedValue = String.valueOf(numVal); //2 should have been delt with on PadString() } final int idx = numVal - 1; if (idx == 1 && monthMod > 0) { monthMod -= 1; } } } else if (nextMask.equals("tt")) { if (useDefaultValues && nextVal == null) { paddedValue = "am"; } isValid = (paddedValue.toLowerCase().equals("am") || paddedValue.toLowerCase().equals("pm")); } else if (nextMask.equals("ss")) { //allow for null value in Date if (useDefaultValues && nextVal == null) { paddedValue = String.valueOf(gc.get(Calendar.SECOND)); /* added to make the testing be more predictable */ if (org.jpedal.objects.javascript.defaultactions.JpedalDefaultJavascript.testingSetStaticDate) { paddedValue = "10"; //new Date(110,10,10,10,10,10); //year,month,date,hrs,mins,secs } paddedValue = padString(paddedValue, 2); isValid = verifyNumberInRange(paddedValue, 0, 59); } else { isValid = verifyNumberInRange(paddedValue, 0, 59); } } else if (nextMask.equals("dd") || nextMask.equals("d")) { isValid = verifyNumberInRange(paddedValue, 0, 31); if (isValid) { dayValue = Integer.parseInt(paddedValue); } } else if (nextMask.equals("yyyy") || nextMask.equals("yy")) { //get a time instance and defaults for all Date values here //add this check to all values except day and month //allow for null value in Date if (useDefaultValues && nextVal == null) { nextVal = String.valueOf(gc.get(Calendar.YEAR)); /* added to make the testing be more predictable */ if (org.jpedal.objects.javascript.defaultactions.JpedalDefaultJavascript.testingSetStaticDate) { nextVal = "2010"; // new Date(110,10,10,10,10,10); //year,month,date,hrs,mins,secs } isValid = verifyNumberInRange(nextVal, 0, 9999); } else { //cannot pad year if (nextMask.length() != nextVal.length()) { if (nextMask.length() > nextVal.length()) { isValid = false; } else { if (nextVal.length() == 4) { isValid = verifyNumberInRange(nextVal, 0, 9999); nextVal = nextVal.substring(2); } } } else { // //07 becomes 2007 // if(nextVal.length()==2){ // int year=Integer.parseInt(nextVal); // if(year<50) // nextVal="20"+nextVal; // else // nextVal="19"+nextVal; // } //note year is not padded out isValid = verifyNumberInRange(nextVal, 0, 9999); } } if (isValid && Integer.parseInt(nextVal) % 4 != 0 && monthMod > 0) { monthMod -= 1; } //stop padded value over-writing underneath paddedValue = nextVal; } else if (nextMask.equals("mmm") || nextMask.equals("mmmm")) { //this needs to handle april apr 4 and 04 -if invalid it uses default (ie May) int idx = -1; //if chars used instead of month number only check first 3 chars if (nextVal.length() >= 3) { for (int i = 0; i != months.length; i++) { nextVal = nextVal.toLowerCase(); final int length = 3; nextVal = nextVal.substring(0, length).toLowerCase(); final String month = months[i].substring(0, length).toLowerCase(); if (nextVal.equals(month)) { idx = i; } } } if (idx == -1) { try { idx = Integer.parseInt(nextVal) - 1; if (idx < 12) { paddedValue = months[idx]; } } catch (final Exception e) { LogWriter.writeLog("Exception in handling JSscript " + e); paddedValue = null; isValid = false; } } else { paddedValue = months[idx]; } if (idx != 1 && monthMod > 0) { monthMod -= 1; } //Check valid month index if (idx > 11) { isValid = false; } else { monthValue = idx; } } else { JSFunction.debug("Mask value >" + nextMask + "< not implemented"); isValid = false; } if (!isValid) { break; } //if passed, add on to result finalValue.append(paddedValue); if (nextSep != null) //not on last one { finalValue.append(nextSep); } } if (monthValue < 0 || monthValue > monthsCount.length || dayValue > monthsCount[monthValue] + monthMod) { isValid = false; } if (isValid) { validValue = finalValue.toString(); } } return validValue; } //must line in range min-max (inclusive so range 0-24 will pass values 0 and 24 and 1 and 23) private static boolean verifyNumberInRange(final String nextVal, final int min, final int max) { boolean valid = true; if (nextVal == null || isNotNumber(nextVal)) { //too long or invalid valid = false; } else { final int number = Integer.parseInt(nextVal); if (number < min || number > max) { valid = false; } } return valid; } //remove double quotes protected static String stripQuotes(String arg) { //lose quotes if (arg.startsWith("\"")) { arg = arg.substring(1, arg.length() - 1); } //allow for \\u00xx if (arg.startsWith("\\u")) { String unicodeVal = arg.substring(2); //Fix for issue where unicode value ends with a space character. if (unicodeVal.endsWith(" ")) { unicodeVal = unicodeVal.substring(0, unicodeVal.length() - 1); } arg = String.valueOf((char) Integer.parseInt(unicodeVal, 16)); } else if (arg.startsWith("\\")) { //and octal final String unicodeVal = arg.substring(1); arg = String.valueOf((char) Integer.parseInt(unicodeVal, 8)); } return arg; } //check it is a number protected static boolean isNotNumber(final String nextVal) { //allow for empty string if (nextVal.isEmpty()) { return true; } //assume false and disprove boolean notNumber = false; final char[] chars = nextVal.toCharArray(); final int count = chars.length; //exit on first char not 0-9 for (int ii = 0; ii < count; ii++) { if (chars[ii] == '.' || chars[ii] == '-' || chars[ii] == ',') { } else if (chars[ii] < 48 || chars[ii] > 57) { ii = count; notNumber = true; } } return notNumber; } public String getValue() { return value; } public int execute(final String js, final String[] args, final int type, final int eventType, final char keyPressed) { return 0; } public String parseJSvariables(String arg) { final String methodToFind = "this.getField("; final int start = arg.indexOf(methodToFind); if (start != -1) { final int nameSt = start + methodToFind.length(); int finish = arg.indexOf(')', nameSt); String name = arg.substring(nameSt, finish); if (name.startsWith("\"")) { name = name.substring(1, name.length() - 1); } //DEFINE the strings to search for within getfield final String valStr = ".value"; if (arg.indexOf(valStr, finish + 1) != -1) { finish = arg.indexOf(valStr, finish + 1) + valStr.length(); final FormObject field = acro.getFormObject(name); arg = arg.substring(0, start) + field.getValue() + arg.substring(finish); } } //check for * / + - % if (!StringUtils.isNumber(arg)) { double firstNum, secondNum; String nextNum; endloop: //TAG so that the we can exit for loop from inside switch for (int i = 0; i < arg.length(); i++) { switch (arg.charAt(i)) { case '*': case '/': case '+': case '-': case '%': firstNum = Double.parseDouble(arg.substring(0, i)); nextNum = getNextNum(arg, i + 1); secondNum = Double.parseDouble(nextNum); final double newValue; switch (arg.charAt(i)) { case '*': newValue = (firstNum * secondNum); break; case '/': newValue = (firstNum / secondNum); break; case '-': newValue = (firstNum - secondNum); break; case '%': newValue = (firstNum % secondNum); break; default://'+' newValue = (firstNum + secondNum); break; } arg = newValue + arg.substring(i + 1 + nextNum.length()); if (StringUtils.isNumber(arg)) { break endloop; } break; } } } return arg; } private static String getNextNum(final String arg, final int s) { int f = -1; ENDLOOP: //TAG to allow us to exit the for loop from inside the switch for (int i = s; i < arg.length(); i++) { switch (arg.charAt(i)) { case '0': case '1': case '2': case '3': case '4': case '.': case '5': case '6': case '7': case '8': case '9': break; default: f = i; break ENDLOOP; } } if (f == -1) { f = arg.length(); } return arg.substring(s, f); } /** */ private static void reportError(final int code, final Object[] args) { final boolean errorReported = false; //report error if (!errorReported) { if (!DecoderOptions.showErrorMessages) { return; } // tell user switch (code) { case ErrorCodes.JSInvalidFormat: JOptionPane.showMessageDialog(null, "The values entered does not match the format of the field [" + args[0] + " ]", "Warning: Javascript Window", JOptionPane.INFORMATION_MESSAGE); break; case ErrorCodes.JSInvalidDateFormat: JOptionPane.showMessageDialog(null, "Invalid date/time: please ensure that the date/time exists. Field [" + args[0] + " ] should match format " + args[1], "Warning: Javascript Window", JOptionPane.INFORMATION_MESSAGE); break; case ErrorCodes.JSInvalidRangeFormat: JOptionPane.showMessageDialog(null, args[1], "Warning: Javascript Window", JOptionPane.INFORMATION_MESSAGE); break; default: JOptionPane.showMessageDialog(null, "The values entered does not match the format of the field", "Warning: Javascript Window", JOptionPane.INFORMATION_MESSAGE); break; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy