
org.jpedal.objects.javascript.functions.JSFunction Maven / Gradle / Ivy
/*
* ===========================================
* 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-2015 IDRsolutions and Contributors.
*
* This file is part of JPedal/JPDF2HTML5
*
This library 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 library 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 library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---------------
* JSFunction.java
* ---------------
*/
package org.jpedal.objects.javascript.functions;
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.utils.LogWriter;
import org.jpedal.utils.StringUtils;
import javax.swing.*;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import org.jpedal.parser.DecoderOptions;
/**
* 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){
//
}
/**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 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(nextValresult) {
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
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"+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
}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));
//
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));
//
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));
//
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));
//
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){
if(LogWriter.isOutput()) {
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(numbermax) {
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;ii57){
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";
final int VALUE = 1;
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
if (code == ErrorCodes.JSInvalidFormat) {
JOptionPane.showMessageDialog(null, "The values entered does not match the format of the field [" + args[0] + " ]",
"Warning: Javascript Window", JOptionPane.INFORMATION_MESSAGE);
} else if (code == 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);
} else if (code == ErrorCodes.JSInvalidRangeFormat) {
JOptionPane.showMessageDialog(null, args[1],
"Warning: Javascript Window",JOptionPane.INFORMATION_MESSAGE);
} else {
JOptionPane.showMessageDialog(null, "The values entered does not match the format of the field",
"Warning: Javascript Window", JOptionPane.INFORMATION_MESSAGE);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy