I am trying to communicate with Excel from a Java/SWT application. I have been able to open a worksheet, open a file and save it but that's about it.
Can anyone point me to some documentation/examples for this? I especially need to know which commands are available. I did try to record macros to inspect. This was useful but did not give me everything I wanted.
This is a sample of what I have been trying so far:
private static OleAutomation openFile(
OleAutomation automation, String fileName) {
Variant workbooks = automation.getProperty(0x0000023c);// get User
// Defined
// Workbooks
Variant[] arguments = new Variant[1];
arguments[0] = new Variant(fileName);
System.out.println("workbooks::\t" + workbooks);
IDispatch p1 = workbooks.getDispatch();
int[] rgdispid = workbooks.getAutomation().getIDsOfNames(new String[] { "Open" });
int dispIdMember = rgdispid[0];
Variant workbook = workbooks.getAutomation().invoke(dispIdMember, arguments);
System.out.println("Opened the Work Book");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
int id = workbook.getAutomation().getIDsOfNames(new String[] { "ActiveSheet" })[0];
System.out.println(id);
Variant sheet = workbook.getAutomation().getProperty(id);
OleAutomation sheetAutomation = sheet.getAutomation();
return (sheetAutomation);
}
Use VBA help MSOffice. Also you can use Object Browser in Office's VB editor.
Not a documentation, but since you asked about the available commands via automation: have you tried the OLE/COM Object viewer that comes with the Windows 2000 resource kit? Download here.
Related
I am using Java to automate the creation and modification of Open Office Calc documents.
I was wondering how to get the number of sheets in a spreadsheet. I can't seem to find any Count, Length, size or similar functions.
Here is my code. Thanks in advance!
public static void openDocument(String filename)
{
try
{
// Get the remote office component context
xContext = Bootstrap.bootstrap();
// Get the remote office service manager
XMultiComponentFactory xMCF = xContext.getServiceManager();
// Get the root frame (i.e. desktop) of openoffice framework.
oDesktop = xMCF.createInstanceWithContext("com.sun.star.frame.Desktop", xContext);
// Desktop has 3 interfaces. The XComponentLoader interface provides ability to load components.
XComponentLoader xCompLoader = (XComponentLoader) UnoRuntime.queryInterface(XComponentLoader.class,
oDesktop);
PropertyValue[] loadProps = new PropertyValue[0];
xSpreadsheetComponent = xCompLoader.loadComponentFromURL(getUpdatedPath(filename), "_blank", 0, loadProps);
xStorable = (XStorable) UnoRuntime.queryInterface(XStorable.class, xSpreadsheetComponent);
xSpreadsheetDocument = (XSpreadsheetDocument) UnoRuntime.queryInterface(XSpreadsheetDocument.class,
xSpreadsheetComponent);
xSpreadsheets = xSpreadsheetDocument.getSheets();
// Need code here to get number of sheets
}
catch (Exception e)
{
e.printStackTrace();
}
This is more of a comment (since I do not know the correct syntax for Java - maybe you need to do a .queryInterface on xSpreadsheets?), but posting as an answer to include an image. Using Bernard Marcelly's object inspection tool XRay (http://bernard.marcelly.perso.sfr.fr/index2.html) shows that an XSpreadsheets object has a method .getCount(). I tested this method using OpenOffice Basic and it works as expected.
I solved my issue using this:
int numberOfSheets = xSpreadsheets.getElementNames().length;
I have a code in Java that opens a excel template by aspose library (it runs perfectly):
import com.aspose.cells.*;
import java.io.*;
public class test
{
public static void main(String[] args) throws Exception
{
System.setProperty("java.awt.headless", "true");
FileInputStream fstream = new FileInputStream("/home/vmlellis/Testes/aspose-cells/template.xlsx");
Workbook workbook = new Workbook(fstream);
workbook.save("final.xlsx");
}
}
After I run this on Ruby with RJB (Ruby Java Bridge):
require 'rjb'
#RJM Loading
JARS = Dir.glob('./jars/*.jar').join(':')
print JARS
Rjb::load(JARS, ['-Xmx512M'])
system = Rjb::import('java.lang.System')
file_input = Rjb::import('java.io.File')
file_input_stream = Rjb::import('java.io.FileInputStream')
workbook = Rjb::import('com.aspose.cells.Workbook')
system.setProperty("java.awt.headless", "true")
file_path = "/home/vmlellis/Testes/aspose-cells/template.xlsx"
file = file_input.new(file_path)
fin = file_input_stream.new(file)
wb = workbook.new(fin)
I get this error:
test.rb:57:in `new': Can't find file: java.io.FileInputStream#693a317a. (FileNotFoundException)
from aspose-test.rb:57:in `<main>'
Why? I run the same code... but in Ruby is not working! How do I fix this?
Update:
In documentation there is the the initializer: Workbook(java.io.InputStreamstream)... but it's not working in RJB. (How is this possible?)
Your program should have worked, but I could not find any reason why it didn't and I am looking into it.
Now the alternate approaches.
Approach 1
Use Workbook(String) constructor instead of Workbook(FileInputStream). This worked flawlessly at my end. The sample code is
require 'rjb'
#RJM Loading
JARS = Dir.glob('/home/saqib/cellslib/*.jar').join(':')
print JARS
Rjb::load(JARS, ['-Xmx512M'])
system = Rjb::import('java.lang.System')
workbook = Rjb::import('com.aspose.cells.Workbook')
system.setProperty("java.awt.headless", "true")
file_path = "/home/saqib/rjb/template.xlsx"
save_path = "/home/saqib/rjb/final.xlsx"
wb = workbook.new(file_path)
wb.save(save_path)
Approach 2
Write a new Java class library. Write all your Aspose.Cells related code in it. Expose very simple and basic methods that needs to be called from Ruby (RJB).
Why?
It is easy to write program in native Java language. If you use RJB, you need to perform a lot of code conversions
It is easy to debug and test in Java.
Usage of RJB will only be limited to calling methods from your own Java library. The RJB code will be small and basic.
Similar Example using own library
Create a new Java project, lets say "cellstest". Add a new public class in it.
package cellstest;
import com.aspose.cells.Workbook;
public class AsposeCellsUtil
{
public String doSomeOpOnWorkbook(String inFile, String outFile)
{
String result = "";
try
{
// Load the workbook
Workbook wb = new Workbook(inFile);
// Do some operation with this workbook
// ..................
// Save the workbook
wb.save(outFile);
// everything ok.
result = "ok";
}
catch(Exception ex)
{
// Return the exception to calling program
result = ex.toString();
}
return result;
}
}
Like this, add as many methods as you like, for each operation.
Build the project and copy the "cellstest.jar" in same folder where you copied Aspose.Cells jar files. You can return a String from your methods and check the return value in Ruby program for success or error code. The Ruby program will now be like
require 'rjb'
#RJM Loading
JARS = Dir.glob('/home/saqib/cellslib/*.jar').join(':')
print JARS
Rjb::load(JARS, ['-Xmx512M'])
system = Rjb::import('java.lang.System')
AsposeCellsUtil = Rjb::import('cellstest.AsposeCellsUtil')
system.setProperty("java.awt.headless", "true")
file_path = "/home/saqib/rjb/template.xlsx"
save_path = "/home/saqib/rjb/final.xlsx"
# initialize instance
asposeCellsUtil = AsposeCellsUtil.new()
# call methods
result = asposeCellsUtil.doSomeOpOnWorkbook(file_path, save_path)
puts result
PS. I work for Aspose as Developer Evangelist.
In your Java code, you pass a file name string into FileInputStream() constructor:
FileInputStream fstream = new FileInputStream("/home/vmlellis/Testes/aspose-cells/template.xlsx");
In your Ruby code, you pass a file object:
file = file_input.new(file_path)
fin = file_input_stream.new(file)
Have you tried to do the same thing as in Java?
fin = file_input_stream.new(file_path)
I'm using rundll32 url.dll,FileProtocolHandler my_file.dotx to open files under Windows.
It works fine with .docx documents, but when I try it with .dotx documents (template documents), it creates a new .docx based on the template.
Just as the normal behavior in the windows explorer : when you double-click on a .dotx template file, it creates a new .docx file based on it. If you want to open the real .dotx file, you have to right-click on it and select "open" instead of "new".
Question is: how to do the same with rundll32? Is there an option in the command to force the opening of the underlying template instead of creating a new document?
Edit: I need a way to do it without C functions, just plain text, in the command line (I'm using Java to do it).
Maybe you can wrap a simple C program around ShellExecute, passing the verb OPEN.
ShellExecute(NULL, TEXT("open"),
TEXT("rundll32.exe"), TEXT("url.dll,FileProtocolHandler pathToGadget"),
NULL, SW_SHOWNORMAL);
I found this example here.
edit:
Since you're doing this in Java - you could try a JNI wrapping of the ShellExceute function like this (from the example I found on The Wannabe Java Rockstar and butchered)
public static boolean execute(String file, String parameters) {
Function shellExecute =
Shell32.getInstance().getFunction(SHELL_EXECUTE.toString());
Int32 ret = new Int32();
shellExecute.invoke(ret, // return value
new Parameter[] {
new Handle(), // hWnd
new Str("open"), // lpOperation
new Str(file), // lpFile
new Str(parameters), // lpParameters
new Str(), // lpDirectory
new Int32(1) // nShowCmd
});
if(ret.getValue() <= 32) {
System.err.println("could not execute ShellExecute: " +
file + ". Return: " + ret.getValue());
}
return (ret.getValue() > 32);
}
public static void main(String[] args) {
ShellExecute.execute("rundll32.exe","url.dll,FileProtocolHandler pathToGadget" );
}
We have a standalone java swing app, in which the user can print something that he drew, on a printer by giving its IP.
Now the requirement is that the app needs to remember the ip that was given the last time by this user.
What I could think of till now is (a brute one though) - keep a log file kind of storage on the client machine, and that everytime the app comes up it reads the last submitted one.
Any suggestions would be helpful.
Thanks in advance.
Here's a tutorial on using the Java Preferences API to achieve what you want.
From the article:
The Java Preferences API provides a
systematic way to handle user and
system preference and configuration
data, e.g. to save user settings,
remember the last value of a field
etc.
I would use this approach over writing any data out explicitly to a file because its platform agnostic.
More or Less that's it. Still you can review the source code for HistoryTextField component of jEdit.
http://www.jedit.org/api/org/gjt/sp/jedit/gui/HistoryTextField.html
A Sample from jEdit source:
public boolean save(Map<String, HistoryModel> models)
{
Log.log(Log.MESSAGE,HistoryModel.class,"Saving history");
File file1 = new File(MiscUtilities.constructPath(
jEdit.getSettingsDirectory(), "#history#save#"));
File file2 = new File(MiscUtilities.constructPath(
jEdit.getSettingsDirectory(), "history"));
if(file2.exists() && file2.lastModified() != historyModTime)
{
Log.log(Log.WARNING,HistoryModel.class,file2
+ " changed on disk; will not save history");
return false;
}
jEdit.backupSettingsFile(file2);
String lineSep = System.getProperty("line.separator");
BufferedWriter out = null;
try
{
out = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(file1), "UTF-8"));
if(models != null)
{
Collection<HistoryModel> values = models.values();
for (HistoryModel model : values)
{
if(model.getSize() == 0)
continue;
out.write('[');
out.write(StandardUtilities.charsToEscapes(
model.getName(),TO_ESCAPE));
out.write(']');
out.write(lineSep);
for(int i = 0; i < model.getSize(); i++)
{
out.write(StandardUtilities.charsToEscapes(
model.getItem(i),
TO_ESCAPE));
out.write(lineSep);
}
}
}
out.close();
/* to avoid data loss, only do this if the above
* completed successfully */
file2.delete();
file1.renameTo(file2);
}
catch(IOException io)
{
Log.log(Log.ERROR,HistoryModel.class,io);
}
finally
{
IOUtilities.closeQuietly(out);
}
historyModTime = file2.lastModified();
return true;
}
Since it is a Swing app., you might launch it using Java Web Start then persist the data using the PersistenceService. Here is a demo. of the PersistenceService.
i dont really recommend this, but you could use the registry also.
I'm using JSch to get files from an SFTP server, but I'm trying to figure out a way to only get the oldest file, and to make sure that it is not currently being written to. The way I imagine myself doing this is first finding which file in the specified remote folder is oldest. I would then check the file size, wait x seconds (probably about 10, just to be safe) and then check it again. If the file size has not changed, I download the file and process it. However, I have no idea how to do this! If anybody knows how to do this, or knows of something else that supports SFTP that has this built-in (I know Apache Commons does, but only does FTPS), it would be greatly appreciated.
Thanks in advance.
Turns out that this is entirely possible in JSch, the hardest part is simply finding the documentation. Code I used is below, hopefully somebody else will find it helpful! (I'm sure there are optimizations to be made, I know, I know. There are also variables that are defined elsewhere, but hopefully anybody that needs this will be able to figure them out!)
public static String oldestFile() {
Vector list = null;
int currentOldestTime;
int nextTime = 2140000000; //Made very big for future-proofing
ChannelSftp.LsEntry lsEntry = null;
SftpATTRS attrs = null;
String nextName = null;
try {
list = Main.chanSftp.ls("*.xml");
if (list.isEmpty()) {
fileFound = false;
}
else {
lsEntry = (ChannelSftp.LsEntry) list.firstElement();
oldestFile = lsEntry.getFilename();
attrs = lsEntry.getAttrs();
currentOldestTime = attrs.getMTime();
for (Object sftpFile : list) {
lsEntry = (ChannelSftp.LsEntry) sftpFile;
nextName = lsEntry.getFilename();
attrs = lsEntry.getAttrs();
nextTime = attrs.getMTime();
if (nextTime < currentOldestTime) {
oldestFile = nextName;
currentOldestTime = nextTime;
}
}
attrs = chanSftp.lstat(Main.oldestFile);
long size1 = attrs.getSize();
System.out.println("-Ensuring file is not being written to (waiting 1 minute)");
Thread.sleep(60000); //Wait a minute to make sure the file size isn't changing
attrs = chanSftp.lstat(Main.oldestFile);
long size2 = attrs.getSize();
if (size1 == size2) {
System.out.println("-It isn't.");
fileFound = true;
}
else {
System.out.println("-It is.");
fileFound = false;
}
}
} catch (Exception ex) {ex.printStackTrace();}
return Main.oldestFile;
}
You can easily do this using edtFTPj/PRO, which supports SFTP.
Simply get a directory listing, and sort the listing by date. If the oldest date isn't in the last few minutes, you can download.
I don't have a direct answer to your question, but it sounds like you want to do something similar to reliable file transfer. This is part of a larger project in Grid computing that is now apparently organized here. I don't know if it includes security features or if you can add them on, but it is an open source project.
calculate the folder size in remote server just call
the ftpFolderSize(ftpFolderSize,client) directory path, and
pass the object FTPClient as a parameter. It will return the
size of folder.
Works only for FTP.
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.io.FileUtils;
long dirSize = 0L; //global variable
private long ftpFolderSize(String directoryPath,FTPClient client){
try{
client.changeWorkingDirectory(directoryPath);
FTPFile[] ftpFiles = client.listFiles();
if(client.changeWorkingDirectory(directoryPath))
{
for (FTPFile ftpFile : ftpFiles) {
if(ftpFile.isFile()){
dirSize = dirSize+ftpFile.getSize();// file size is calculated
}
else if(ftpFile.isDirectory())
{
dirSize = dirSize + 4096;//folder minimum size is 4kb
ftpFolderSize(directoryPath+"/"+ftpFile.getName(),client);
}
}
}
}catch (Exception e) {
e.printStackTrace();
}
return dirSize;
}