Error while creating large dropdown in excel using ColdFusion - coldfusion

This code I have written for creating large dropdown in ColdFusion, but it is not working on my end. Could any one please help me rectify my problem. The new code is
<cfquery name="getPOP" datasource="l_webalc">
select distinct center_code from alc_pop
</cfquery>
<cfset countryName= ArrayNew(1)>
<cfloop query="getPOP">
<cfset arrayappend(countryName, getPOP.center_code)>
</cfloop>
<script>
workbook = new HSSFWorkbook();
realSheet = workbook.createSheet("Sheet xls");
hidden = workbook.createSheet("hidden");
for (int i = 0, length= countryName.length; i < length; i++) {
String name = countryName[i];
HSSFRow row = hidden.createRow(i);
HSSFCell cell = row.createCell(0);
cell.setCellValue(name);
}
namedCell = workbook.createName();
namedCell.setNameName("hidden");
namedCell.setRefersToFormula("hidden!A1:A" + countryName.length);
constraint = DVConstraint.createFormulaListConstraint("hidden");
addressList = new CellRangeAddressList(0, 0, 0, 0);
validation = new HSSFDataValidation(addressList, constraint);
workbook.setSheetHidden(1, true);
realSheet.addValidationData(validation);
stream = new FileOutputStream("c:\\range.xls");
workbook.write(stream);
stream.close();
</script>
Update 1:
(From other thread) I am getting this error message:
function keyword is missing in FUNCTION declaration. The CFML compiler
was processing: A script statement beginning with HSSFWorkbook on line
32, column 1. A script statement beginning with function on line 31,
column 9. A cfscript tag beginning on line 30, column 2.
Update 2:
Again I have modified this code and now the new error is
"The value hidden not A1:A cannot be converted to a number."
I edited the objects as mentioned in the comments and also changed the script to cfscript. Please help me to rectify this error.
<cfscript>
workbook = createObject("java", "org.apache.poi.hssf.usermodel.HSSFWorkbook");
realSheet = workbook.createSheet("Sheet xls");
hidden = workbook.createSheet("hidden");
for (i = 1; i <= arrayLen(countryName); i++){
name = countryName[i];
row = hidden.createRow(i);
cell = row.createCell(0);
cell.setCellValue(name);
}
namedCell = workbook.createName();
namedCell.setNameName("hidden");
namedCell.setRefersToFormula("hidden!A1:A"+arrayLen(countryName));
dv = createObject("java", "org.apache.poi.hssf.usermodel.DVConstraint");
constraint = dv.createFormulaListConstraint("hidden");
addressList = cellRangeList.init(0, 0, 0, 0);
validation = dataValidation.init(addressList, constraint);
workbook.setSheetHidden(1, true);
realSheet.addValidationData(validation);
stream = new FileOutputStream("c:\\range.xls");
workbook.write(stream);
stream.close();
</cfscript>
Update 3:
I have updated the code to fix the mentioned issues and and am now getting this error
"The setSheetHidden method was not found ..."
on the following line:
workbook.setSheetHidden(1, true);

There are several problems with your code. Though java syntax is similar, you cannot just copy and paste a java example and expect it to run in cfscript. You need to make some adjustments first. (Note: I am assuming script was just a typo for cfscript).
In java, you can instantiate an object using the keyword "new" ie new SomeClassName(). In CF, the new keyword can only be used with cfc's. To create a java object, you must use createObject instead. To instantiate it, call the init(...) method. It is a special method in CF that invoke's the constructor of a java class with whatever parameters you supply, ie
createObject("java", "path.to.SomeClassName").init();
To use static methods such as DVConstraint.createFormulaListConstraint(), you must also use createObject. While the java code does not create an new instance of that class, you must still use createObject to get a reference to the DVConstraint class, in CF, before you can invoke any of its methods. Note: Because it is static, no need to call init() first. ie
dv = createObject("java", "org.apache.poi.hssf.usermodel.DVConstraint");
dv.createFormulaListConstraint(...);
Java classes are organized into packages. In java classes, the full path to any referenced classes are imported at the top of the java code (not visible in the example you are using). In CF you need to use the full path in your createObject call. (Important: Paths are cAsE sEnsItIvE).
For example, instead of new HSSFWorkbook() use:
createObject("java", "org.apache.poi.hssf.usermodel.HSSFWorkbook");
If you are not sure of the path, just do a search on "POI TheClassName". Odds are the first result will be the POI JavaDocs, which show the full path at the top of each page like this:
java.lang.Object
|---org.apache.poi.ss.util.CellRangeAddressList
Unlike CF, java is strongly typed, which means you must declare a variable's type as well as it's name. For example, this line declares a variable row as type HSSFRow
HSSFRow row = hidden.createRow(i);
Since CF is typeless, it does not require a type. So running that same code in cfscript will cause the cryptic error "function keyword is missing...". The solution is to drop the variable type and just do a straight variable assignment:
row = hidden.createRow(i);
Java array indexes start at zero (0), while CF starts at one (1), so you need to fix the indexes in your for loop:
for (i = 1; i <= arrayLen(countryName); i++)
Java uses + to concatenate strings, whereas CF uses &. So you need to change the operator here "hidden!A1:A" + countryName.length. Otherwise CF will think you are trying to add two numbers, which will obviously throw an error because the first part is a string.
Assuming no version conflicts, the java example should work after you make those those adjustments.

"The setSheetHidden method was not found ..."
Just use Javacast function for boolean arguments:
workbook.setSheetHidden(1, javacast("boolean",true));

Related

Java code migration error using com.jayway.jsonpath.JsonPath

I am using Java code and converting the code in ColdFusion. There are some challenges where I am stuck. This is one function I have in Java:
import com.jayway.jsonpath.JsonPath;
import net.minidev.json.JSONArray;
import net.minidev.json.JSONStyle;
private static String getDetails(String instaDetailsElement) {
String jsonResponse = instaDetailsElement.split(" = ")[1];
JSONArray mediaArray = JsonPath.read(jsonResponse, "$.entry_data.PostPage[:1].graphql.shortcode_media");
String returnJsonString = mediaArray.toJSONString(new JSONStyle(JSONStyle.FLAG_IGNORE_NULL));
System.out.println(returnJsonString);
return returnJsonString;
}
These two lines are giving me some trouble:
var mediaArray = JsonPath.read(jsonResponse, "$.entry_data.PostPage[:1].graphql.shortcode_media");
var returnJsonString = mediaArray.toJSONString(new JSONStyle(JSONStyle.FLAG_IGNORE_NULL));
Here is what I attempted so far. I loaded the jar library for JSON path and tried using it like this:
Application.cfc settings
<cfset this.javaSettings = {LoadPaths = ["cfc/jar"], loadColdFusionClassPath = true, reloadOnChange = false}>
CF Code:
public any function getDetails(String instaDetailsElement) {
var jsonResponse = instaDetailsElement.split(" = ")[1];
var JsonPath = Createobject("java","com.jayway.jsonpath.JsonPath");
writedump(application);
var mediaArray = JsonPath.read(jsonResponse, "$.entry_data.PostPage[:1].graphql.shortcode_media");
writedump(mediaArray); abort;
var returnJsonString = mediaArray.toJSONString(new JSONStyle(JSONStyle.FLAG_IGNORE_NULL));
return returnJsonString;
}
I'm able to view the class methods when I dump the JsonPath object (screen shot), but when I try to call JsonPath.read() I get this error:
No matching Method for read(string, string) found for
com.jayway.jsonpath.JsonPath
TL;DR;
No matching method for read(string, string) found for com.jayway.jsonpath.JsonPath
Technically the error message is correct: there is no read() method that accepts two strings (even though that's how it's used in the java code). The method actually expects three arguments:
Pass in an empty array for the 3rd argument:
JsonPath.read(jsonResponse, "$.entry_data.PostPage[:1].graphql.shortcode_media", []);
Explanation:
String jsonResponse = instaDetailsElement.split(" = ")[1];
JsonPath.read(jsonResponse, "$.entry_data.PostPage[:1].graphql.shortcode_media")
If there really is no read(String, String) method, you might wonder why the java code works at all, since that's exactly what it uses. It works due to a special feature of java.
The documentation shows the overloaded read(..) method actually has three parameters, but one of them is special:
read(String json,
String jsonPath,
Predicate... filters)
Notice the ... after the class name (Predicate)? It's a construct called "varargs" (or variable number of arguments):
You can use a construct called varargs to pass an arbitrary number of values to a method. You use varargs when you don't know how many of a
particular type of argument will be passed to the method. It's a
shortcut to creating an array manually ...
To use varargs, you follow the type of the last parameter by an ellipsis (three dots, ...), then a space, and the parameter name. The
method can then be called with any number of that parameter,
including none.
So in java you're allowed to omit the third argument entirely and call read(String, String) with two strings. ColdFusion doesn't support that syntax, because it creates too much ambiguity. So in lieu of omitting the argument, you can pass in an empty array instead:
JsonPath.read(jsonResponse, "$.entry_data.PostPage[:1].graphql.shortcode_media", []);
(Since this has turned into two questions in one thread, I'm separating the second answer out for clarity ...)
var returnJsonString = mediaArray.toJSONString(new JSONStyle(JSONStyle.FLAG_IGNORE_NULL));
As for translating the JSONStyle code, it helps to unpack nested code from the inside out. Then tackle each piece separately:
mediaArray.toJSONString(new JSONStyle( JSONStyle.FLAG_IGNORE_NULL ));
mediaArray.toJSONString( new JSONStyle( JSONStyle.FLAG_IGNORE_NULL ) )
mediaArray.toJSONString( new JSONStyle( JSONStyle.FLAG_IGNORE_NULL ) )
Piece #1
Uses a static field of the JSONStyle class named FLAG_IGNORE_NULL. To access the field, create a reference to that class:
JsonStyle = createObject("java", "net.minidev.json.JSONStyle");
writeDump(JSONStyle.FLAG_IGNORE_NULL);
Piece #2
Creates a brand new instance of the JSONStyle class, using the static field from above. Use createObject() to create the new instance, passing the static field into the psuedo constructor init():
newJsonStyle = createObject("java", "net.minidev.json.JSONStyle").init(JSONStyle.FLAG_IGNORE_NULL);
writeDump( newJsonStyle );
Piece #3
All that's left is calling the JSONArray.toJSONString() method with the JSONStyle object you just created:
result = mediaArray.toJSONString( newJsonStyle );
writeDump(result);

Does CF not support Java constructors with variable number of arguments and any known workarounds?

We have a project that deals with files of various encodings. I am using BOMInputStream to skip UTF-8 byte order markers. The existing code works but needs to also support UTF-16 variations. The most straight forward approach is to pass the BOMInputStream constructor multiple ByteOrderMarkers.
Per documentation...
BOMInputStream bomIn = new BOMInputStream(in,
ByteOrderMark.UTF_16LE,
ByteOrderMark.UTF_16BE,
ByteOrderMark.UTF_32LE,
ByteOrderMark.UTF_32BE);
The constructor signature uses variable arguments:
public BOMInputStream(InputStream delegate,
ByteOrderMark... boms)
However, when I try to call this constructor using the following code
<cfset var fis = createObject("java", "java.io.FileInputStream").init(arguments.filePath) />
<cfset var boms = createObject("java", "org.apache.commons.io.ByteOrderMark") />
<cfset var bomins = createObject("java", "org.apache.commons.io.input.BOMInputStream").init(fis, boms.UTF_8, boms.UTF_16LE, boms.UTF_16BE) />
I get the following error...
Unable to find a constructor for class org.apache.commons.io.input.BOMInputStream that accepts parameters of type ( java.io.FileInputStream, org.apache.commons.io.ByteOrderMark, org.apache.commons.io.ByteOrderMark, org.apache.commons.io.ByteOrderMark ).
I have tried just one BOM argument as well and get same error with fewer arguments in the error. So it appears that CF can't call Java constructors with unlimited arguments. Is that correct and, if so, is there any known work around?
Of course the moment I post an idea comes to me. It looks like these variables are accessed through an array in the Java object being called. I just changed the CF code to pass an array of BOMs instead of individual arguments and it worked as expected.
<cfset var bomins = createObject("java", "org.apache.commons.io.input.BOMInputStream").init(
fis,
[boms.UTF_8, boms.UTF_16LE, boms.UTF_16BE]
) />

CF10 Twitter4j lookupUsers method was not found or method is overloaded

I am using CFML and Twitter4j to return timelines and lists.
I want to return the data from a call to lookupUsers(java.lang.String[] screenNames)
via Twitter4j.
I have tried the :-
strList = createObject("java", "java.util.ArrayList");
strList.add(strOriginUser);
originUser = t4j.lookupUsers(strList);
And :-
strUserString = JavaCast("String", strOriginUser);
originUser = t4j.lookupUsers(strUserString);
I know the t4j Object is working as I already use it to get timelines etc but here it is for completeness :-
public function init_twitter() {
//CONFIGURE twitter4j
configBuilder = createObject("java", "twitter4j.conf.ConfigurationBuilder");
configBuilder.setOAuthConsumerKey(#application.twitter_consumer_key#);
configBuilder.setOAuthConsumerSecret(#application.twitter_consumer_secret#);
configBuilder.setOAuthAccessToken(#application.twitter_access_token#);
configBuilder.setOAuthAccessTokenSecret(#application.twitter_access_token_secret#);
configBuilder.setIncludeEntitiesEnabled(true);
configBuilder.setJSONStoreEnabled(true);
config = configBuilder.build();
twitterFactory = createObject("java", "twitter4j.TwitterFactory").init(config);
variables.t4j = twitterFactory.getInstance();
return this;
}
The twitter4j documentations is:-
ResponseList<User> lookupUsers(java.lang.String[] screenNames) throws TwitterException
Return up to 100 users worth of extended information, specified by either ID, screen name, or combination of the two. The author's most recent status (if the authenticating user has permission) will be returned inline.
This method calls http://api.twitter.com/1.1/users/lookup.json
Parameters:
screenNames - Specifies the screen names of the users to return.
Returns:
users
It looks like you are trying to pass an ArrayList object into lookupUsers but that method only accepts String[] (an array of Strings) as an argument. So unless CFML does the conversion, I don't think it's going to work.
From a cursory glance at the ColdFusion docs, it looks like CFML can implicitly convert a CFML Array to a Java array, so perhaps the following would work:
screenNames = arrayNew(1);
screenNames[1] = 'Fry';
originUser = t4j.lookupUsers(screenNames);
Alternatively, if you want to keep on using a list there is an ArrayList#toArray(T[]) which could be useful, although I can't say how useful that would be in the CFML.
N.B. Please excuse my CFML code snippet.

ColdFusion 9 Dynamic Method Call

I am trying to work out the correct <cfscript> syntax for calling a dynamic method within ColdFusion 9. I have tried a number of variations and had a good search around.
<cfinvoke> is clearly the tag I want, sadly however I cannot use this within my pure cfscript component as it was implemented in ColdFusion 10.
i.e coldfusion 9 dynamically call method
I have tried the following within my CFC:
/** Validate the method name **/
var resources = getResources();
if (structKeyExists(variables.resources, name)) {
variables.resourceActive[name] = true;
var reflectionMethod = resources[name];
var result = "#reflectionMethod.getMethodName()#"(argumentCollection = params);
}
Where the return value of reflectionMethod.getMethodName() is the method name I want to call. It is 100% returning the correct value (the name of the method) where that method is correctly defined and accessible,
My error is a syntax error on that line.
You don't want to get the method name, you want to get the actual method, eg something like:
function getMethod(string method){
return variables[method];
}
The call that, thus:
theMethod = getMethod(variableHoldingMethodName);
result = theMethod();
Unfortunately one cannot simply do this:
result = getMethod(variableFoldingMethodName)();
Or:
result = myObject[variableFoldingMethodName]();
As the CF parser doesn't like the double-up of the parentheses or brackets.
The caveat with the method I suggested is that it pulls the method out of the CFC, so it will be running in the context of the calling code, not the CFC instance. Depending on the code in the method, this might or might not matter.
Another alternative is to inject a statically-named method INTO the object, eg:
dynamicName = "foo"; // for example
myObject.staticName = myObject[dynamicName];
result = myObject.staticName(); // is actually calling foo();
Assuming the method is in your current (variables) scope, you could try:
var result = variables[reflectionMethod.getMethodName()](argumentCollection = params);

output variable stored in database

I'm storing the page content in a database table. The page content also includes some CF variables (for example "...this vendor provides services to #VARIABLES.vendorLocale#").
VARIABLES.vendorLocal is set on the page based on a URL string.
Next a CFC is accessed to get the corresponding page text from the database.
And this is then output on the page: #qryPageContent.c_content#
But #VARIABLES.vendorLocale# is showing up as is, not as the actual variable. Is there anyway to get a "variable within a variable" to be output correctly?
This is on a CF9 server.
If you have a string i.e.
variables.vendorLocal = 'foo';
variables.saveMe = 'This is a string for supplier "#variables.vendorLocal#'"' ;
WriteOutput(variables.saveMe); // This is a string for locale "foo"
then coldfusion will attempt to parse that to insert whatever variable variables.vendorLocale is. To get around this, you can use a placeholder string that is not likely to be used elsewhere. Commonly you'll see [[NAME]] used for this purpose, so in this example
variables.saveMe = 'This is a string for supplier "[[VENDORLOCALE]]'"' ;
WriteOutput(variables.saveMe); // This is a string for supplier "[[VENDORLOCALE]]"
Now you've got that you can then later on replace it for your value
variables.vendorLocal = 'bar';
variables.loadedString = Replace(variables.saveMe,'[[VENDORLOCALE]]',variables.vendorLocal);
WriteOutput(variables.loadedString); // This is a string for locale "bar"
I hope this is of help
There are lots of reasons storing code itself in the database is a bad idea, but that's not your question, so I won't go into that. One way to accomplish what you want is to take the code you have stored as as string, write a temporary file, include that file in the page, then delete that temporary file. For instance, here's a little UDF that implements that concept:
<cfscript>
function dynamicInclude(cfmlcode){
var pathToInclude = createUUID() & ".cfm";
var pathToWrite = expandPath(pathToInclude);
fileWrite(pathToWrite,arguments.cfmlcode);
include pathToInclude;
fileDelete(pathToWrite);
}
language = "CFML";
somecfml = "This has some <b>#language#</b> in it";
writeOutput(dynamicInclude(somecfml));
</cfscript>