/* * Copyright (C) 2000 - 2008 TagServlet Ltd * * This file is part of Open BlueDragon (OpenBD) CFML Server Engine. * * OpenBD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * Free Software Foundation,version 3. * * OpenBD 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenBD. If not, see http://www.gnu.org/licenses/ * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with any of the JARS listed in the README.txt (or a modified version of * (that library), containing parts covered by the terms of that JAR, the * licensors of this Program grant you additional permission to convey the * resulting work. * README.txt @ http://www.openbluedragon.org/license/README.txt * * http://www.openbluedragon.org/ */ package com.naryx.tagfusion.cfm.tag; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; import com.naryx.tagfusion.cfm.engine.Javacast; import com.naryx.tagfusion.cfm.engine.cfArrayData; import com.naryx.tagfusion.cfm.engine.cfBinaryData; import com.naryx.tagfusion.cfm.engine.cfBooleanData; import com.naryx.tagfusion.cfm.engine.cfData; import com.naryx.tagfusion.cfm.engine.cfDateData; import com.naryx.tagfusion.cfm.engine.cfFixedArrayData; import com.naryx.tagfusion.cfm.engine.cfJavaArrayData; import com.naryx.tagfusion.cfm.engine.cfJavaObjectData; import com.naryx.tagfusion.cfm.engine.cfJavaStructData; import com.naryx.tagfusion.cfm.engine.cfNullData; import com.naryx.tagfusion.cfm.engine.cfNumberData; import com.naryx.tagfusion.cfm.engine.cfQueryResultData; import com.naryx.tagfusion.cfm.engine.cfStringData; import com.naryx.tagfusion.cfm.engine.cfStructData; import com.naryx.tagfusion.cfm.engine.cfmRunTimeException; import com.naryx.tagfusion.cfm.engine.dataNotSupportedException; import com.naryx.tagfusion.cfm.xml.cfXmlData; import com.naryx.tagfusion.cfm.xml.ws.encoding.ser.QueryBean; public final class tagUtils extends Object { /* * Returns a List of sprted page numbers that have selected. If all pages are * selected, then null is returned * * 1, 2, 3-5, 7-12 */ public static List<Integer> getNumberListSorted(String pagesStr) throws NumberFormatException { Set<Integer> set = getNumberSet( pagesStr ); List<Integer> list = new ArrayList<Integer>( set.size() ); Iterator<Integer> it = set.iterator(); while ( it.hasNext() ) list.add( it.next() ); Collections.sort( list ); return list; } /* * Returns a Set of page numbers that have selected. If all pages are * selected, then null is returned * * 1, 2, 3-5, 7-12 */ public static Set<Integer> getNumberSet(String pagesStr) throws NumberFormatException { if (pagesStr.length() == 0) { return null; } else { HashSet<Integer> pages = new HashSet<Integer>(); String[] pageArr = pagesStr.split(","); for (int i = 0; i < pageArr.length; i++) { String nextPage = pageArr[i].trim(); int rangeIndx = pageArr[i].indexOf('-'); if (rangeIndx < 0) { pages.add(Integer.parseInt(nextPage)); } else { int start = Integer.parseInt(nextPage.substring(0, rangeIndx)); int end = Integer.parseInt(nextPage.substring(rangeIndx + 1)); for (int j = start; j <= end; j++) { pages.add(j); } } } return pages; } } public static String getLastToken(String rhs) { return getLastToken(rhs, "."); } public static String getLastToken(String rhs, String tok) { int c1 = rhs.lastIndexOf(tok); if (c1 == -1) return rhs; else return rhs.substring(c1 + 1); } public static String trimError(String error) { if (error == null) return "null"; int c1 = error.indexOf("java.sql.SQLException"); if (c1 != -1) return error.substring(c1 + 21).trim(); else return error; } public static String removeExtra(String rhs) { if (rhs.length() >= 2 && rhs.charAt(1) == '#' && ((rhs.charAt(0) == '\"' && rhs.charAt(rhs.length() - 1) == '\"') || (rhs.charAt(0) == '\'' && rhs.charAt(rhs.length() - 1) == '\''))) rhs = rhs.substring(1, rhs.length() - 1); return com.nary.util.string.replaceString(rhs, "#", ""); } public static String arrayIndexClean(String indx) { if (indx.length() >= 2 && indx.charAt(0) == '\"' && indx.charAt(indx.length() - 1) == '\"') return indx.substring(1, indx.length() - 1); else return "#" + indx + "#"; } private static String BODMAS[] = { "(", "+", "-", "*", "/", "!", "%", "&", "[", "]", " eq ", " equal ", " is ", " is not ", " not equal ", " neq ", " not ", " contains ", " does not contain ", " greater than ", " less than ", " gt", " lt ", " gte ", " ge ", " lte ", " le ", " greater than or equal to ", " less than or equal to " }; public static boolean isExpression(String RHS) { RHS = RHS.toLowerCase(); for (int x = 0; x < BODMAS.length; x++) if (RHS.indexOf(BODMAS[x]) != -1) return true; return false; } private static String NAMES[] = { "variables.", "form.", "url.", "cookie.", "cgi.", "client.", "session.", "server", "application.", "caller.", "request." }; public static String getQualifiedName(String name) { name = name.toLowerCase(); for (int x = 0; x < NAMES.length; x++) { if (name.indexOf(NAMES[x]) == 0) return name; } return "variables." + name; } /** * The conversion from a Java object class to a CFML type is based on the * following: * * http://livedocs.macromedia.com/coldfusion/6.1/htmldocs/java35.htm * * Note the following errors in the above-referenced documentation: * * 1. A Java Long/long is converted to a CFML Real Number (not Integer). 2. A * java.util.List is converted to a CFML Array (not comma delimited list). 3. * A Java byte[] is converted to a CFML Binary (not Array). */ public static cfData convertToCfData(Object obj) { if (obj instanceof cfData) { return (cfData) obj; } else if (obj == null) { return cfNullData.JAVA_NULL; } else if (obj instanceof java.lang.String) { return new cfStringData((String) obj); } else if (obj instanceof java.lang.Boolean) { return cfBooleanData.getcfBooleanData((Boolean) obj); } else if (obj instanceof java.lang.Character) { return new cfStringData(((Character) obj).toString()); } else if (obj instanceof java.lang.Byte) { return new cfStringData(((Byte) obj).toString()); } else if (obj instanceof java.lang.Short) { return new cfNumberData((Short) obj); } else if (obj instanceof java.lang.Integer) { return new cfNumberData(((Integer) obj)); } else if (obj instanceof java.lang.Long) { return new cfNumberData((Long) obj); } else if (obj instanceof java.lang.Double) { return new cfNumberData((Double) obj); } else if (obj instanceof java.lang.Float) { return new cfNumberData((Float) obj); } else if (obj instanceof java.util.Date) { return new cfDateData(((java.util.Date) obj).getTime()); } else if (obj instanceof java.util.Map) { java.util.Map<?, ?> map = (java.util.Map<?, ?>) obj; return new cfJavaStructData(map); } else if (obj instanceof java.util.List) { java.util.List<Object> v = (java.util.List<Object>) obj; return new cfJavaArrayData(v); } else if (obj.getClass().isArray()) { if (obj.getClass().getName().equals("[B")) { return new cfBinaryData((byte[]) obj); } else { cfData cfdata = tagUtilsJava.convertToCfData(obj); if (cfdata != null) return cfdata; return cfArrayData.createArray(obj); } } else if (obj instanceof QueryBean) { return convertToCFQuery((QueryBean) obj); } else if (cfXmlData.isXmlObject(obj)) { return cfXmlData.newInstance(obj, true); } return new cfJavaObjectData(obj); }// convertToCfData() /** * converts a QueryBean to a CFQUERY */ private static cfQueryResultData convertToCFQuery(QueryBean qb) { cfQueryResultData rtn = new cfQueryResultData(qb.getColumnList(), "QueryNew()"); // get the column names int noCols = qb.getColumnList().length; // populate tablerows // get the no. of row by getting the no. of rows in the data set int noRows = qb.getData().length; rtn.addRow(noRows); for (int i = 1; i <= noRows; i++) { for (int j = 1; j <= noCols; j++) rtn.setCell(i, j, convertToCfData(qb.getData()[i - 1][j - 1])); } return rtn; } /** * converts the given cfData to an instance of the specified Class. Returns * null if the conversion is not possible */ public static Object convertCFtoJava(cfData _cfdata, Class<?> _class ) { return convertCFtoJava( _cfdata, _class, true ); } public static Object convertCFtoJava(cfData _cfdata, Class<?> _class, boolean _applyJavacast ) { String className = _class.getName(); try { if (className.equals("java.lang.Object") || (className.equals("java.io.Serializable") && (_cfdata.getDataType() != cfData.CFJAVAOBJECTDATA))) { if ( _applyJavacast && _cfdata.getJavaCast() != null ){ Javacast javacast = _cfdata.getJavaCast(); _class = javacast.getCastClass(); return convertCFtoJava( _cfdata, _class, false ); }else{ return getNatural(_cfdata); } } else if (className.equals("java.lang.Comparable") && ((_cfdata.getDataType() == cfData.CFNUMBERDATA) || (_cfdata.getDataType() == cfData.CFSTRINGDATA) || (_cfdata.getDataType() == cfData.CFDATEDATA) || (_cfdata.getDataType() == cfData.CFNULLDATA))) { return getNatural(_cfdata); } else if (className.equals("java.lang.Cloneable") && _cfdata.getDataType() == cfData.CFDATEDATA) { return getNatural(_cfdata); } else if (className.equals("java.lang.Number") && (_cfdata.getDataType() == cfData.CFNUMBERDATA)) { return getNatural(_cfdata); } else if (className.equals("boolean") || className.equals("java.lang.Boolean")) { return new Boolean(_cfdata.getBoolean()); } else if (className.equals("java.lang.String")) { return _cfdata.getString(); } else if (className.equals("char") || className.equals("java.lang.Character")) { String chStr = _cfdata.getString(); return new Character(chStr.charAt(0)); } else if (className.equals("int") || className.equals("java.lang.Integer")) { return new Integer(_cfdata.getInt()); } else if (className.equals("double") || className.equals("java.lang.Double")) { return new Double(_cfdata.getDouble()); } else if (className.equals("float") || className.equals("java.lang.Float")) { return new Float(_cfdata.getDouble()); } else if (className.equals("long") || className.equals("java.lang.Long")) { return new Long(_cfdata.getLong()); } else if (className.equals("short") || className.equals("java.lang.Short")) { return new Short((short) _cfdata.getInt()); } else if (className.equals("byte") || className.equals("java.lang.Byte")) { return new Byte((byte) _cfdata.getInt()); } else if (_cfdata.getDataType() != cfData.CFJAVAOBJECTDATA && className.equals("java.util.Date")) { return new java.util.Date(_cfdata.getDateData().getLong()); // if the object is a java.util.Vector wrapped up by a cfJavaObjectData // we don't want to catch it here } else if (_cfdata.getDataType() != cfData.CFJAVAOBJECTDATA && !(_cfdata instanceof cfJavaArrayData) && _class.isAssignableFrom(Class.forName("java.util.Vector"))) { return convertToVector(_cfdata); } else if (_cfdata.getDataType() != cfData.CFJAVAOBJECTDATA && _class.isArray()) { if (_cfdata.getDataType() == cfData.CFBINARYDATA && _class.getName().equals("[B")) { return ((cfBinaryData) _cfdata).getByteArray(); } else if (_cfdata.getDataType() == cfData.CFARRAYDATA) { return getArray((cfArrayData) _cfdata, _class); } else { return null; } } else if (_class.isInstance(_cfdata)) { // cfArrayData implements java.util.List/System.Collections.IList // cfStructData implements Map // ... return _cfdata; } if (_cfdata instanceof cfJavaObjectData) { Object javaObj = ((cfJavaObjectData) _cfdata).getInstance(); if ((javaObj != null) && (_class.isInstance(javaObj))) { return javaObj; } } } catch (Exception ignored) { } // allow to return null. return null; }// convertCFtoJava() private static Vector<Object> convertToVector(cfData _cfdata) { if (_cfdata.getDataType() == cfData.CFARRAYDATA) { Vector<Object> converted = new Vector<Object>(); cfArrayData cfarray = (cfArrayData) _cfdata; Object nextObj; cfData next; int arrayLen = cfarray.size(); for (int i = 1; i <= arrayLen; i++) { next = cfarray.getElement(i); nextObj = getNatural(next, true); if (nextObj == null) { // couldn't convert return null; } converted.addElement(nextObj); } return converted; } else { return null; } } /** * If "deep" is set to true, objects contained within cfStructData and * cfArrayData are also converted to natural objects. If "forceDouble" is set * to true, all numeric cfData objects are forced into Double objects * irrespective of their significant digits (or lack thereof). If * "preferObjectArray" is true cfArrayData types are converted to Object[] * instead of a VectorArrayList. */ public static Object getNatural(cfData _cfdata) { return tagUtilsNatural.getNatural(_cfdata, false, false, false); } public static Object getNatural(cfData _cfdata, boolean deep) { return tagUtilsNatural.getNatural(_cfdata, deep, false, false); } public static Object getNatural(cfData _cfdata, boolean deep, boolean forceDouble) { return tagUtilsNatural.getNatural(_cfdata, deep, forceDouble, false); } public static Object getNatural(cfData _cfdata, boolean deep, boolean forceDouble, boolean preferObjectArray) { return tagUtilsNatural.getNatural(_cfdata, deep, forceDouble, preferObjectArray); } public static Map<String, Object> getNaturalMap(cfStructData _struct) { return tagUtilsNatural.getNaturalMap(_struct, true, false, false); } /** * converts the given cfArrayData to an array matching the given Class type. * Returns null if the match cannot be done. */ private static Object getArray(cfArrayData _cfarray, Class<?> _class) { if (_cfarray instanceof cfFixedArrayData && _cfarray.getInstanceClass().equals(_class)) { try { return _cfarray.getInstance(); } catch (cfmRunTimeException e) { return null; } } Object nextObj; // Determine the number of dimensions in the class we're converting the // array to. String classname = _class.getName(); int numToDim = 0; while (classname.charAt(numToDim) == '[') numToDim++; // Get the number of dimensions in the array int numDimensions = _cfarray.getDimension(); // If the dimensions don't match then return null if (numDimensions != numToDim) return null; // Determine the length of each dimension int[] dimLens = new int[numDimensions]; cfArrayData innerArray = _cfarray; for (int i = 0; i < _cfarray.getDimension(); i++) { dimLens[i] = innerArray.size(); if (i + 1 < _cfarray.getDimension()) innerArray = (cfArrayData) innerArray.getElement(i + 1); } // Remove all but one dimension before calling translateToClass. classname = classname.substring(numToDim - 1); Class<?> arrayType = translateToClass(classname); // Create the array Object newArray = Array.newInstance(arrayType, dimLens); // Fill in the array int[] pos = new int[numDimensions]; if ( _cfarray.size() > 0 ){ while (true) { // Get the next value cfData next = _cfarray.getElement(pos[0] + 1); for (int i = 1; i < numDimensions; i++) { next = ((cfArrayData) next).getElement(pos[i] + 1); } // Set the value to null if necessary if (next instanceof cfNullData && !_class.isPrimitive()) { nextObj = null; } else { nextObj = convertCFtoJava(next, arrayType); if (nextObj == null) return null; // couldn't convert } // Set the value in the array Object innerMostArray = newArray; for (int i = 0; i < numDimensions - 1; i++) innerMostArray = Array.get(newArray, pos[i]); Array.set(innerMostArray, pos[numDimensions - 1], nextObj); // Increment the position boolean done = true; for (int i = 0; i < numDimensions; i++) { pos[i]++; if (pos[i] < dimLens[i]) { done = false; break; } pos[i] = 0; } // If we've reached the end of all of the dimensions then we are done if (done) break; } } return newArray; }// getArray // translates an Array name to a Class that represents the type private static Class<?> translateToClass(String _classname) { if (_classname.startsWith("[L")) { try { return Class.forName(_classname.substring(2, _classname.length() - 1)); } catch (ClassNotFoundException unlikely) { return null; } } else if (_classname.equals("[B")) { return byte.class; } else if (_classname.equals("[C")) { return char.class; } else if (_classname.equals("[D")) { return double.class; } else if (_classname.equals("[F")) { return float.class; } else if (_classname.equals("[I")) { return int.class; } else if (_classname.equals("[J")) { return long.class; } else if (_classname.equals("[S")) { return short.class; } else if (_classname.equals("[Z")) { return boolean.class; } else { return null; } } // a valid UUID has the form XXXXXXXX-XXXX-XXXX-XXXXXXXXXXXXXXXX (8-4-4-16), // where each "X" is a valid hexadecimal digit (0-9 or A-F) public static boolean isUUID(cfData data) throws dataNotSupportedException { char[] dataChars = getChars(data, 35); if (dataChars == null) return false; for (int i = 19; i < dataChars.length; i++) { if (!isHexDigit(dataChars[i])) return false; } return true; } // a valid "guid" has the form XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX // (8-4-4-4-12), // where each "X" is a valid hexadecimal digit (0-9 or A-F) public static boolean isGUID(cfData data) throws dataNotSupportedException { char[] dataChars = getChars(data, 36); if (dataChars == null) return false; if (dataChars[23] != '-') return false; for (int i = 19; i < 23; i++) { if (!isHexDigit(dataChars[i])) return false; } for (int i = 24; i < dataChars.length; i++) { if (!isHexDigit(dataChars[i])) return false; } return true; } public static boolean isXmlString(String str) { return str.trim().toLowerCase().startsWith("<?xml"); } // check the common portion (8-4-4-) of the guid/UUID private static char[] getChars(cfData data, int length) throws dataNotSupportedException { if (data.getDataType() != cfData.CFSTRINGDATA) return null; char[] dataChars = data.getString().toUpperCase().toCharArray(); if (dataChars.length != length) return null; if ((dataChars[8] != '-') || (dataChars[13] != '-') || (dataChars[18] != '-')) return null; for (int i = 0; i < 8; i++) { if (!isHexDigit(dataChars[i])) return null; } for (int i = 9; i < 13; i++) { if (!isHexDigit(dataChars[i])) return null; } for (int i = 14; i < 18; i++) { if (!isHexDigit(dataChars[i])) return null; } return dataChars; } // only accepts uppercase letters A-F private static boolean isHexDigit(char c) { return (c >= 48 && c <= 57) || (c >= 65 && c <= 70); } }