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

com.adobe.xfa.formcalc.BuiltinString Maven / Gradle / Ivy

The newest version!
/*
 * ADOBE CONFIDENTIAL
 *
 * Copyright 2007 Adobe Systems Incorporated All Rights Reserved.
 *
 * NOTICE: All information contained herein is, and remains the property of
 * Adobe Systems Incorporated and its suppliers, if any. The intellectual and
 * technical concepts contained herein are proprietary to Adobe Systems
 * Incorporated and its suppliers and may be covered by U.S. and Foreign
 * Patents, patents in process, and are protected by trade secret or copyright
 * law. Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained from
 * Adobe Systems Incorporated.
 */
package com.adobe.xfa.formcalc;


import com.adobe.xfa.ut.LcData;
import com.adobe.xfa.ut.PictureFmt;
import com.adobe.xfa.ut.ExFull;

import java.util.UUID;


/**
 * This class defines static methods to implement
 * the FormCalc string calculations.
 *
 *  S T R I N G    F U N C T I O N S
 *      at, concat, format, left, len, lower, ltrim, parse, replace, right,
 *      rtrim, space, str, stuff, substr, upper, wordnum.
 *
 * @author Mike P. Tardif
 *
 * @exclude from published api.
 */
final class BuiltinString {

	/*
	 *  Disallow instances of this class.
	 */
	private BuiltinString() {
	}

	static final long QUINTILLION = 1000000000000000000L;


	/*
	 * At
	 *  This function returns the (1-based origin) index of one given string
	 *  in another given string.
	 */
	static void At(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 2);
			Builtins.maxArgs(nArgs, 2);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// retrieve arguments.
			//
			String s1 = oParser.getString(oArgSym[0]);
			String s2 = oParser.getString(oArgSym[1]);
			int nUniIndex = 0;
			int nChrIndex = s1.indexOf(s2);
			if (nChrIndex >= 0) {
				for (int i = 0; i < s1.length(); i++) {
					if (nChrIndex == i)
						break;
					if (s1.codePointAt(i) <= 0xFFFF)
						nUniIndex++;
				}
				nUniIndex++;
			}
			oRetSym = new CalcSymbol((double) nUniIndex);
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 * Concat
	 *  This function returns the concatenation of a set of non-null strings.
	 */
	static void Concat(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check for error-valued and return-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			try {
				//
				// ensure not all args are null.
				//
				Builtins.limitAllNullArgs(oParser, oArgSym);
				//
				// compute the concatenation of the given set.
				//
				oRetSym = computeCat(oParser, nArgs, oArgSym);
			} catch (CalcException e) {
				//
				// all args are null -- return null.
				//
				oRetSym = new CalcSymbol();
			}
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	static CalcSymbol computeCat(CalcParser oParser, int nArgs, CalcSymbol[] oArgSym) {
		CalcSymbol oRetSym = null;
		try {
			StringBuilder s = new StringBuilder();
			for (int i = 0; i < nArgs; i++) {
				switch (oArgSym[i].getType()) {
				case CalcSymbol.TypeAccessor:
					CalcSymbol[] oSym = oParser.moScriptHost.getItemValue(
							oArgSym[i].getName(), oArgSym[i].getObjValues());
					CalcSymbol pCat = computeCat(oParser, oSym.length, oSym);
					s.append(oParser.getString(pCat));
					for (int j = oSym.length - 1; j >= 0; j--)
						CalcSymbol.delete(oSym[j], oParser);
					break;
				case CalcSymbol.TypeNull:
					break;
				default:
					s.append(oParser.getString(oArgSym[i]));
					break;
				}
			}
			oRetSym = new CalcSymbol(s.toString());
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		return oRetSym;
	}


	/*
	 * Format
	 *  This function returns the result of formatting the specified
	 *	source string according to the specified picture.
	 */
	static void Format(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 2);
			Builtins.maxArgs(nArgs, 2);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, 1, oArgSym);
			//
			// retrieve and normalize arguments.  Reversed args to conform
			// to published spec -- Mike Tardif, Nov 2003.
			//
			String m = oParser.getString(oArgSym[0]);
			String s = oParser.getString(oArgSym[1]);
			//
			// Watson 2322019: Runtime error when we assigned the null value to a form variable
			// When pArgSym[1].getType() is jfCalcTypeAccessor or jfCalcTypeReference, need to
			// get the actual type that is accessed or referred to.
			if (oParser.getActualType(oArgSym[1]).getType() == CalcSymbol.TypeNull)
				s = null;
			String l = oParser.msLocaleName;
			//
			// optional data sources still not implemented.
			//
			StringBuilder sFormat = new StringBuilder();
			try {
				PictureFmt oPict = new PictureFmt(l);
				oPict.format(s, m, sFormat);
			} catch (ExFull oException) {
				String sErr = oException.toString();
				if (sErr != null) {
					throw new CalcException(sErr);
				}
				else {
					throw new CalcException();
				}
			}
			oRetSym = new CalcSymbol(sFormat.toString());
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 * Left
	 *  This function returns the specified number of characters from
	 *  the left of the given string.
	 */
	static void Left(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 2);
			Builtins.maxArgs(nArgs, 2);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// retrieve and normalize arguments.
			//
			String s = oParser.getString(oArgSym[0]);
			int n = (int) oParser.getNumeric(oArgSym[1]);
			if (n < 0)
				n = 0;
			if (n > s.length())
				n = s.length();
			int nUniChar = 0;
			int i = 0;
			for (; i < s.length(); i++) {
				if (nUniChar == n)
					break;
				if (s.codePointAt(i) <= 0xFFFF)
					nUniChar++;
			}
			//
			// compute left most part of string.
			//
			oRetSym = new CalcSymbol(s.substring(0, i));
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 * Len
	 *  This function returns the length of the given string.
	 */
	static void Len(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 1);
			Builtins.maxArgs(nArgs, 1);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// compute the length of string.
			//
			String s = oParser.getString(oArgSym[0]);
			int	nUniChar = 0;
			for (int i = 0; i < s.length(); i++) {
				if (s.codePointAt(i) <= 0xFFFF)
					nUniChar++;
			}
			oRetSym = new CalcSymbol(nUniChar);
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 * Lower
	 *  This function returns the lowercase equivalent of the given string.
	 */
	static void Lower(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 1);
			Builtins.maxArgs(nArgs, 2);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// retrieve arguments, ignoring locale.
			//
			StringBuilder s = new StringBuilder(oParser.getString(oArgSym[0]));
			//
			// twiddle bits of all ASCII, Latin1
			// and fullwidth lowercase letters.
			//
			for (int i = 0; i < s.length(); i++) {
				char n = s.charAt(i);
				if ('\u0040' < n && n < '\u005b')
					s.setCharAt(i, (char) (n | 0x20));
				else if ('\u00bf' < n && n < '\u00d7'
										|| '\u00d7' < n && n < '\u00df')
					s.setCharAt(i, (char) (n | 0x20));
				else if ('\uff20' < n && n < '\uff3b') {
					s.setCharAt(i, (char) ((n & ~0x20) | 0x40));
				}
			}
			oRetSym = new CalcSymbol(s.toString());
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 * Ltrim
	 *  This function returns the given string with all
	 *  left most spaces trimmed.
	 */
	static void Ltrim(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 1);
			Builtins.maxArgs(nArgs, 1);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// retrieve arguments, and trim left-most spaces.
			//
			String s = oParser.getString(oArgSym[0]);
    		int i = 0;
    		while (i < s.length()) {
    			int cUni = s.codePointAt(i);
    			if (! Character.isWhitespace(cUni) && ! Character.isSpaceChar(cUni))
    				break;
				i += (cUni <= 0xFFFF) ? 1 : 2;
    		}
    		oRetSym = new CalcSymbol(s.substring(i));
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 * NumFmt
	 *  This function returns a format number string,
	 *  given a number format style.
	 *
	 */
	static void NumFmt(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 0);
			Builtins.maxArgs(nArgs, 2);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// retrieve and normalize arguments.
			//
			int n = (nArgs > 0) ? (int) oParser.getNumeric(oArgSym[0]) : 0;
			String l = (nArgs > 1) ? oParser.getString(oArgSym[1])
										: oParser.msLocaleName;
			//
			// get the locale's date format style.
			//
			LcData data = new LcData(l);
			int o = LcData.withPrecision(~0) | LcData.WITH_GROUPINGS;
			oRetSym = new CalcSymbol(data.getNumberFormat(n, o));
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 * Parse
	 *  This function returns the result of parsing the specified
	 *	source string according to the specified picture.
	 */
	static void Parse(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 2);
			Builtins.maxArgs(nArgs, 2);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, 1, oArgSym);
			//
			// retrieve and normalize arguments.  Reversed args to conform
			// to published spec -- Mike Tardif, Nov 2003.
			//
			String m = oParser.getString(oArgSym[0]);
			String s = oParser.getString(oArgSym[1]);
			if (oArgSym[1].getType() == CalcSymbol.TypeNull)
				s = null;
			String l = oParser.msLocaleName;
			String sFormat = null;
			try {
				PictureFmt oPict = new PictureFmt(l);
				sFormat = oPict.parse(s, m, null);
			} catch (ExFull oException) {
				String sErr = oException.toString();
				if (sErr != null) {
					throw new CalcException(sErr);
				}
				else {
					throw new CalcException();
				}
			}
			if (sFormat == null)
				oRetSym = new CalcSymbol();
			else
				oRetSym = new CalcSymbol(sFormat);
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 * Replace
	 *  This function returns the given string with all occurences
	 *  of the old string replaced with the new string.
	 */
	static void Replace(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 2);
			Builtins.maxArgs(nArgs, 3);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, 2, oArgSym);
			//
			// retrieve arguments.
			//
			String str = oParser.getString(oArgSym[0]);
			String old = oParser.getString(oArgSym[1]);
			String rep = (nArgs > 2) ? oParser.getString(oArgSym[2]) : "";
			StringBuilder s = new StringBuilder();
			//
			// replace all occurrences of the old string with the replacement
			// string in the given string argument.
			//

			//CL#729807 - jfString.ReplaceAll is now called in C++ code
			//  but not changing String.replaceAll here as that considers cutStr 
			//  as a regex - this CL was a performance related one - functionality
			//  wise it was (and remains) same ..
			int k = old.length();
			if (k > 0 && ! old.equals(rep)) {
				int i = 0;
				while ((i = str.indexOf(old)) >= 0) {
					s.append(str, 0, i);
					s.append(rep);
				    i += k;
					str = str.substring(i);
				}
			}
			s.append(str);
			//CL#729807
			oRetSym = new CalcSymbol(s.toString());
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 * Right
	 *  This function returns the specified number of characters from
	 *  the right of the given string.
	 */
	static void Right(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 2);
			Builtins.maxArgs(nArgs, 2);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// retrieve and normalize arguments.
			//
			String s = oParser.getString(oArgSym[0]);
			int n = (int) oParser.getNumeric(oArgSym[1]);
			int len = s.length();
			if (n > len)
				n = len;
			if (n < 0)
				n = 0;
			//
			// compute right most part of string.
			//
			int nUniChar = 0;
			int i = 0;
			for (; i < s.length(); i++) {
				if (s.codePointAt(i) <= 0xFFFF)
    				nUniChar++;
			}
	        if (n > nUniChar)
	            n = nUniChar;
	        n = nUniChar - n;
			nUniChar = 0;
			for (i = 0; i < s.length(); i++) {
				if (nUniChar == n)
					break;
				if (s.codePointAt(i) <= 0xFFFF)
    				nUniChar++;
			}
			oRetSym = new CalcSymbol(s.substring(i));
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 * Rtrim
	 *  This function returns the given string with all
	 *  right most spaces trimmed.
	 */
	static void Rtrim(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 1);
			Builtins.maxArgs(nArgs, 1);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// retrieve arguments, and trim right-most Unicode spaces.
			//
    		String s = oParser.getString(oArgSym[0]);
    		for (int i = 0; i < s.length(); ) {
    			int k = i;
    			int cUni = s.codePointAt(i);
				i += (cUni <= 0xFFFF) ? 1 : 2;
    			if (Character.isWhitespace(cUni) || Character.isSpaceChar(cUni)) {
    				int m = i;
    				for (int j = i; j < s.length(); m = j) {
    					cUni = s.codePointAt(j);
        				j += (cUni <= 0xFFFF) ? 1 : 2;
    					if (Character.isWhitespace(cUni))
    						continue;
    					if (Character.isSpaceChar(cUni))
    						continue;
    					break;
    			}
    			if (m == s.length())
    				s = s.substring(0, k);    // trim it
    			else
    				i = m;                    // advance to next non whitespace.
    			}
    		}
    		oRetSym = new CalcSymbol(s);
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 * Space
	 *  This function returns a string with the given number of spaces.
	 */
	static void Space(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 1);
			Builtins.maxArgs(nArgs, 1);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// retrieve arguments, and generate string of spaces.
			//
			int n = (int) oParser.getNumeric(oArgSym[0]);
			if (n < 0)
				n = 0;
			char[] s = new char[n];
			for (int i = 0; i < n; i++)
				s[i] = ' ';
			oRetSym = new CalcSymbol(new String(s));
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 * format a double value n as a string of width w and precision p.
	 */
	private static String dblToStr(double n, int w, int p) {
		StringBuilder oStr = new StringBuilder(FormCalcUtil.dblToStr(n, p));
		FormCalcUtil.trimRadix(oStr);
		FormCalcUtil.trimSign(oStr);
		//
		// Blank-pad or null-out result to given width.
		//
		int k = oStr.length();
		if (k < w) {
			for (int i = k; i < w; i++)
				oStr.insert(0, ' ');
		}
		else if (k > w) {
			oStr.setLength(0);
			for (int i = 0; i < w; i++)
				oStr.append('*');
		}
		return oStr.toString();
	}


	/*
	 * Str
	 *  This function converts a given number to a string of given width
	 *  and precision.
	 */
	static void Str(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 1);
			Builtins.maxArgs(nArgs, 3);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// retrieve and normalize arguments.
			//
			double n = oParser.getNumeric(oArgSym[0]);
			int w = (nArgs > 1) ? (int) oParser.getNumeric(oArgSym[1]) : 10;
			int p = (nArgs > 2) ? (int) oParser.getNumeric(oArgSym[2]) : 0;
			if (w < 0)
				w = 10;
			if (p < 0)
				p = 0;
			//
			// format number to string.
			//
			oRetSym = new CalcSymbol(dblToStr(n, w, p));
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 *  Stuff
	 *      This function replaces a number of characters in a given string
	 *      with a given insert.
	 */
	static void Stuff(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 3);
			Builtins.maxArgs(nArgs, 4);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, 3, oArgSym);
			//
			// retrieve and normalize arguments.
			//
			String str = oParser.getString(oArgSym[0]);
			int loc = (int) oParser.getNumeric(oArgSym[1]);
			loc--;
			if (loc < 0)
				loc = 0;
			int len = (int) oParser.getNumeric(oArgSym[2]);
			if (len < 0)
				len = 0;
			String ins = (nArgs > 3) ? oParser.getString(oArgSym[3]) : "";
	        //
	        // compute the starting index.
	        //
			int nUniChar = 0;
	        int k = 0;
			int i;
			for (i = 0; i < str.length(); ) {
				if (nUniChar++ >= loc)
					break;
				k = i;
				i += (str.codePointAt(i) <= 0xFFFF) ? 1 : 2;
			}
			loc = (nUniChar <= loc) ? k : i;
	        //
	        // compute the length.
	        //
			nUniChar = 0;
			for (i = loc; i < str.length(); ) {
				if (nUniChar++ >= len)
					break;
				i += (str.codePointAt(i) <= 0xFFFF) ? 1 : 2;
			}
			len = i - loc;
			//
			// stuff everything:
			//      the left part is str.substring(0, loc).
			//      the middle part is all of ins.
			//      the right part, if any, is str.substring(loc + len).
			//
			StringBuilder s = new StringBuilder(str.substring(0, loc));
			s.append(ins);
			if (loc + len < str.length())
				s.append(str, loc + len, str.length());
			oRetSym = new CalcSymbol(s.toString());
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 *  Substr
	 *      This function returns a substring of the given string.
	 */
	static void Substr(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 3);
			Builtins.maxArgs(nArgs, 3);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// retrieve and normalize arguments.
			//
			String str = oParser.getString(oArgSym[0]);
			int loc = (int) oParser.getNumeric(oArgSym[1]);
			loc--;
			if (loc < 0)
				loc = 0;
			int len = (int) oParser.getNumeric(oArgSym[2]);
			if (len < 0)
				len = 0;
	        //
	        // compute the starting index.
	        //
			int nUniChar = 0;
	        int k = 0;
			int i;
			for (i = 0; i < str.length(); ) {
				if (nUniChar++ >= loc)
					break;
				k = i;
				i += (str.codePointAt(i) <= 0xFFFF) ? 1 : 2;
			}
			loc = (nUniChar <= loc) ? k : i;
	        //
	        // compute the length.
	        //
			nUniChar = 0;
			for (i = loc; i < str.length();) {
				if (nUniChar++ >= len)
					break;
				i += (str.codePointAt(i) <= 0xFFFF) ? 1 : 2;
			}
			//
			// extract the substring.
			//
			str = str.substring(loc, i);
			oRetSym = new CalcSymbol(str);
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 *  Uuid
	 *      This function returns uuid.
	 *
	 */
	static void Uuid(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 0);
			Builtins.maxArgs(nArgs, 1);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// retrieve arguments, ignoring locale.
			//
			int n = (nArgs > 0) ? (int) oParser.getNumeric(oArgSym[0]) : 0;
			//
			// Generate the uuid string.
			//
			String u = UUID.randomUUID().toString();
			//
			// Swallow all dashes if requested.
			//
			if (n == 0) {
				StringBuilder sUuid = new StringBuilder(u);
				
				int nDash = sUuid.indexOf("-");
				while (nDash >= 0) {
					sUuid.deleteCharAt(nDash);
					nDash = sUuid.lastIndexOf("-");
				}
				
				u = sUuid.toString();
			}
			oRetSym = new CalcSymbol(u);
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}


	/*
	 *  Upper
	 *      This function returns the uppercase equivalent of the given string.
	 *
	 */
	static void Upper(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 1);
			Builtins.maxArgs(nArgs, 2);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// retrieve arguments, ignoring locale.
			//
			StringBuilder s = new StringBuilder(oParser.getString(oArgSym[0]));
			//
			// twiddle bits of all ASCII, LATIN1 and fullwidth uppercase letters
			// while remembering that we are operating on UTF-8 data!
			//
			for (int i = 0; i < s.length(); i++) {
				char n = s.charAt(i);
				if ('\u0060' < n && n < '\u007b')
					s.setCharAt(i, (char) (n & ~0x20));
				else if ('\u00df' < n && n < '\u00f7'
										|| '\u00f7' < n && n < '\u00ff')
					s.setCharAt(i, (char) (n & ~0x20));
				else if ('\uff40' < n && n < '\uff5b') {
					s.setCharAt(i, (char) ((n & ~0x40) | 0x20));
				}
			}
			oRetSym = new CalcSymbol(s.toString());
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}

		//
		// The names of the numbers: 0-9
		//
		static final String Ones[] = {
			"Zero", "One", "Two",   "Three", "Four",
			"Five", "Six", "Seven", "Eight", "Nine"
		};
		//
		// The names of the numbers: 10-19
		//
		static final String Teens[] = {
			"Ten",     "Eleven",  "Twelve",    "Thirteen", "Fourteen",
			"Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"
		};
		//
		// The names of the numbers: 0,10,20,...,90,100.
		//
		static final String Tens[] = {
			"Zero",  "Ten",   "Twenty",  "Thirty", "Forty",
			"Fifty", "Sixty", "Seventy", "Eighty", "Ninety", "Hundred"
		};
		//
		// The names of the numbers:
		//     1000,1000000,1000000000,...,1000000000000000,1000000000000000000.
		//
		static final String Thousands[] = {
			"Thousand", "Million",     "Billion",
			"Trillion", "Quadrillion", "Quintillion"
		};
		//
		// The conjunction for the dollar and cent parts of name.
		//
		static final String Ands[] = {
			"",     "And "     /* used by FF99 */
	//      "And ", "And "     /* used by Filler4.x */
		};
		//
		// The dollar name.
		//
		static final String Dollars[] = {
			"Dollar"
		};
		//
		// The cent name.
		//
		static final String Cents[] = {
			"Cent"
		};
		//
		// The character delimiter used after thousands names,
		// e.g. one million, twenty thousand, three hundred thirty-six.
		//
		static final String Comma[] = {
			""                  /* used by FF99 */
	//      ","                 /* used by Filler4.x */
		};
		//
		// The character delimiter used after teen names,
		// e.g., thirty-six.
		//
		static final String Hyphen = "-";
		//
		// The character delimiter used after non-teen names,
		// e.g., three hundred.
		//
		static final String Space = " ";


	/*
	 * Convert number to a formatted word equivalent.
	 *
	 * Note: there is ABSOLUTELY no point in externalizing the following names
	 * to a resource file.  The grammar that wordum uses is inherently English.
	 * There's no way, by simply localizing these name in French, for example,
	 * that you'll be able to get wordnum to generate "mille" instead of
	 * "un mille" as it would do, or "deux milles" instead of "deux mille",
	 * or even generate "soixante-onze" or "quatre-vingt-quinze".
	 * What is required is a mechanism to externalize the grammar.  IBM's
	 * alphaWorks lab has done this; see
	 *      http://www.alphaWorks.ibm.com/tech/rbnf
	 * This ought to convince everyone that this isn't a trivial exercise, and
	 * difficult to justify at this time.  Better instead to localize all of
	 * wordnum on an has-needed basis.
	 */
	private static String wordnum(double n, int f) {
		//
		// If number is NaN or negative, then return string of asterisks (*).
		//
		if (Double.isNaN(n) || Double.isInfinite(n) || n < 0.) {
			return "*********";
		}
		//
		// If format is invalid, then assume format style 0.
		//
		if (f < 0 || 2 < f) {
			f = 0;
		}
		//
		// Round the number to two decimal places.
		//
		String sStr = FormCalcUtil.dblToStr(n, 2);
		n = FormCalcUtil.strToDbl(sStr, false);
		//
		// Round off cents, independent of format.
		//
		long dollars = (long) n;
		int cents = (int) ((n - (double) dollars) * 100. + .5);
		if (cents > 100) {
			dollars += 1;
			cents -= 100;
		}
		//
		// If preceding led to nonsense values Then its probably because
		// we've reached numbers whose magnitude are beyond arithmetic
		// resolution of architecture, so bail out.
		//
		if (cents > 100) {
			return "*********";
		}
		StringBuilder s = new StringBuilder();
		int thousands = Thousands.length;
		for (long div = QUINTILLION; div > 0; div /= 1000) {
			long number = (long) (dollars / div);
			int hundreds = (int) (number / 100);
			int tens = (int) ((number - hundreds * 100) / 10);
			int ones = (int) (number - hundreds * 100 - tens * 10);
			dollars -= number * div;
			if (hundreds > 0) {
				s.append(Ones[hundreds]);
				s.append(Space);
				s.append(Tens[10]);
				s.append(Space);
				if (tens > 0 || ones > 0)
					s.append(Ands[0]);
			}
			if (tens > 0) {
				s.append((tens == 1) ? Teens[ones] : Tens[tens]);
				s.append((ones > 0 && tens != 1) ? Hyphen : Space);
			}
			if (ones > 0 && tens != 1) {
				if (tens > 0 && ones > 0) {
					// safe since Ones contains true literal constants
					String o = Ones[ones];
					s.append(Character.toLowerCase(o.charAt(0)));
					s.append(o, 1, o.length());
				}
				else {
					s.append(Ones[ones]);
				}
				s.append(Space);
			}
			thousands--;
			if (thousands >= 0 && number > 0) {
				s.append(Thousands[thousands]);
				s.append(Comma[0]);
				s.append(Space);
			}
		}
		//
		// If less than one then use zero.
		//
		if (n < 1.) {
			s.append(Ones[0]);
			s.append(Space);
		}
		//
		// Factor in format:
		//     0 => "One Hundred Twenty-three"
		//     1 => "One Hundred Twenty-three Dollars"
		//     2 => "One Hundred Twenty-three Dollars And Forty Cents"
		//
		if (f == 0) {
			//
			// Remove trailing space.
			//
			if (s.length() > 0) {
				s.setLength(s.length() - Space.length());
			}
		}
		else if (f == 1 || f == 2) {
			//
			// Append dollar CalcSymbol.
			//
			s.append(Dollars[0]);
			if ((int) n != 1)
				s.append('s');
			//
			// Append cents.
			//
			if (f == 2) {
				s.append(Space);
				s.append(Ands[1]);
				int tens = (int) (cents / 10);
				int ones = (int) (cents - tens * 10);
				if (tens > 0) {
					s.append((tens == 1) ? Teens[ones] : Tens[tens]);
				}
				if (tens != 1) {
					if (tens > 0 && ones > 0) {
						// safe since Ones contains true literal constants
						String o = Ones[ones];
						s.append(Hyphen);
						s.append(Character.toLowerCase(o.charAt(1)));
						s.append(o, 1, o.length());
					}
					else if (tens == 0) {
						s.append(Ones[ones]);
					}
				}
				s.append(Space);
				s.append(Cents[0]);
				if (cents != 1.)
					s.append('s');
			}
		}
		return s.toString();
	}


	static void WordNum(CalcParser oParser, CalcSymbol[] oArgSym) {
		final int nArgs = oArgSym.length;
		CalcSymbol oRetSym = null;
		try {
			//
			// check the number of args vs the number required.
			//
			Builtins.minArgs(nArgs, 1);
			Builtins.maxArgs(nArgs, 3);
			//
			// check for error-valued, return-valued and null-valued args.
			//
			Builtins.limitExceptionArgs(oArgSym);
			Builtins.limitNullArgs(oParser, nArgs, oArgSym);
			//
			// retrieve and normalize arguments, ignoring locale.
			//
			String s = oParser.getString(oArgSym[0]).trim();
			double n = FormCalcUtil.strToDbl(s, true);
			int f = (nArgs > 1) ? (int) oParser.getNumeric(oArgSym[1]) : 0;
			oRetSym = new CalcSymbol(wordnum(n, f));
		} catch (CalcException e) {
			oRetSym = e.getSymbol();
			if (oRetSym.getType() != CalcSymbol.TypeNull)
				oParser.mbInThrow = true;
		}
		//
		// push the result on the stack.
		//
		oParser.mStack.push(oRetSym);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy