/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ro.nextreports.designer.util;
import ro.nextreports.engine.Report;
import ro.nextreports.engine.ReportLayout;
import ro.nextreports.engine.util.ReportUtil;
import ro.nextreports.engine.band.Band;
import ro.nextreports.engine.band.BandElement;
import ro.nextreports.engine.band.BarcodeBandElement;
import ro.nextreports.engine.band.ChartBandElement;
import ro.nextreports.engine.band.ImageBandElement;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.zip.ZipOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.jdesktop.jdic.desktop.Desktop;
import ro.nextreports.designer.Globals;
import ro.nextreports.designer.LayoutHelper;
import ro.nextreports.designer.action.report.layout.export.ExportAction;
/**
* Created by IntelliJ IDEA.
* User: mihai.panaitescu
* Date: May 18, 2006
* Time: 11:43:06 AM
*/
public class FileUtil {
// Deletes all files and subdirectories under dir.
// Returns true if all deletions were successful.
// If a deletion fails, the method stops attempting to delete and returns false.
public static boolean deleteDir(File dir) {
if (dir.isDirectory()) {
String[] children = dir.list();
for (String file : children) {
boolean success = deleteDir(new File(dir, file));
if (!success) {
return false;
}
}
}
// The directory is now empty so delete it
return dir.delete();
}
public static File[] listFilesAsArray(File directory, FilenameFilter filter, boolean recurse) {
Collection<File> files = listFiles(directory, filter, recurse);
File[] arr = new File[files.size()];
return files.toArray(arr);
}
public static List<File> listFiles(File directory, FilenameFilter filter, boolean recurse) {
// List of files / directories
List<File> files = new ArrayList<File>();
// Get files / directories in the directory
File[] entries = directory.listFiles();
if (entries == null) {
return files;
}
// Go over entries
for (File entry : entries) {
// If there is no filter or the filter accepts the
// file / directory, add it to the list
if (filter == null || filter.accept(directory, entry.getName())) {
files.add(entry);
}
// If the file is a directory and the recurse flag
// is set, recurse into the directory
if (recurse && entry.isDirectory()) {
files.addAll(listFiles(entry, filter, recurse));
}
}
// Return collection of files
return files;
}
public static void zip(List<String> fileNames, String outFileName) {
// Create a buffer for reading the files
byte[] buf = new byte[1024];
try {
// Create the ZIP file
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(outFileName));
// Compress the files
for (String fileName : fileNames) {
FileInputStream in = new FileInputStream(fileName);
// Add ZIP entry to output stream.
out.putNextEntry(new ZipEntry(fileName));
// Transfer bytes from the file to the ZIP file
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
// Complete the entry
out.closeEntry();
in.close();
}
// Complete the ZIP file
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void zip(List<String> fileNames, String outFileName, String withoutBase) {
// Create a buffer for reading the files
byte[] buf = new byte[1024];
try {
// Create the ZIP file
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(outFileName));
// Compress the files
for (String fileName : fileNames) {
FileInputStream in = new FileInputStream(fileName);
// Add ZIP entry to output stream.
if (withoutBase != null) {
fileName = fileName.substring(withoutBase.length() + 1);
}
out.putNextEntry(new ZipEntry(fileName));
// Transfer bytes from the file to the ZIP file
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
// Complete the entry
out.closeEntry();
in.close();
}
// Complete the ZIP file
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void unzip(String filename, String destination) {
ZipInputStream zipinputstream;
try {
zipinputstream = new ZipInputStream(new FileInputStream(filename));
} catch (FileNotFoundException e) {
e.printStackTrace();
return;
}
unzip(zipinputstream, destination);
}
public static void unzip(ZipInputStream zipinputstream, String destination) {
try {
byte[] buf = new byte[1024];
ZipEntry zipentry = zipinputstream.getNextEntry();
while (zipentry != null) {
String entryName = zipentry.getName();
File newFile = new File(entryName);
String path = destination + File.separator + entryName;
// take care to create the directories
String dirs = path.substring(0, path.lastIndexOf(File.separator));
new File(dirs).mkdirs();
if (!zipentry.isDirectory()) {
FileOutputStream fileoutputstream = new FileOutputStream(path);
int n;
while ((n = zipinputstream.read(buf, 0, 1024)) > -1) {
fileoutputstream.write(buf, 0, n);
}
fileoutputstream.close();
zipinputstream.closeEntry();
} else {
new File(path).mkdirs();
}
zipentry = zipinputstream.getNextEntry();
}
zipinputstream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void copy(File source, File dest) throws IOException {
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(source);
out = new FileOutputStream(dest);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
public static void copyToDir(File source, File dest) throws IOException {
copyToDir(source, dest, false);
}
public static void copyToDir(File source, File dest, boolean overwrite) throws IOException {
File destFile = new File(dest.getAbsolutePath() + File.separator + source.getName());
if (!overwrite && destFile.exists()) {
throw new IOException("File '" + destFile.getName() + "' already exists!");
}
copy(source, destFile);
}
// copy all files recursively from directory source to directory dest
// dest directory must be already created!
public static void copyDirToDir(File source, File dest) throws IOException {
if (!source.isDirectory() || !dest.isDirectory()) {
return;
}
List<File> files = listFiles(source, null ,true);
String sourcePath = source.getAbsolutePath();
String destPath = dest.getAbsolutePath();
for (File file : files) {
String filePath =file.getAbsolutePath();
if (sourcePath.equals(filePath)) {
continue;
}
String newPath = destPath + File.separator + filePath.substring(sourcePath.length());
File destFile = new File(newPath);
if (file.isDirectory()) {
destFile.mkdirs();
} else {
copy(file, destFile);
}
}
}
public static ArrayList<String> tail(String fileName, int lineCount) {
return tail(fileName, lineCount, 2000);
}
/**
* Given a byte array this method:
* a. creates a String out of it
* b. reverses the string
* c. extracts the lines
* d. characters in extracted line will be in reverse order,
* so it reverses the line just before storing in Vector.
* <p/>
* On extracting required numer of lines, this method returns TRUE,
* Else it returns FALSE.
*
* @param bytearray byte array
* @param lineCount number of lines
* @param lastNlines array of lines
* @return true if extracted required number of lines
*/
private static boolean parseLinesFromLast(byte[] bytearray, int lineCount, ArrayList<String> lastNlines) {
String lastNChars = new String(bytearray);
StringBuffer sb = new StringBuffer(lastNChars);
lastNChars = sb.reverse().toString();
StringTokenizer tokens = new StringTokenizer(lastNChars, "\n");
while (tokens.hasMoreTokens()) {
StringBuffer sbLine = new StringBuffer(tokens.nextToken());
lastNlines.add(sbLine.reverse().toString());
if (lastNlines.size() == lineCount) {
return true;//indicates we got 'lineCount' lines
}
}
return false; //indicates didn't read 'lineCount' lines
}
/**
* Reads last N lines from the given file. File reading is done in chunks.
* <p/>
* Constraints:
* 1 Minimize the number of file reads -- Avoid reading the complete file
* to get last few lines.
* 2 Minimize the JVM in-memory usage -- Avoid storing the complete file
* info in in-memory.
* <p/>
* Approach: Read a chunk of characters from end of file. One chunk should
* contain multiple lines. Reverse this chunk and extract the lines.
* Repeat this until you get required number of last N lines. In this way
* we read and store only the required part of the file.
* <p/>
* 1 Create a RandomAccessFile.
* 2 Get the position of last character using (i.e length-1). Let this be curPos.
* 3 Move the cursor to fromPos = (curPos - chunkSize). Use seek().
* 4 If fromPos is less than or equal to ZERO then go to step-5. Else go to step-6
* 5 Read characters from beginning of file to curPos. Go to step-9.
* 6 Read 'chunksize' characters from fromPos.
* 7 Extract the lines. On reading required N lines go to step-9.
* 8 Repeat step 3 to 7 until
* a. N lines are read.
* OR
* b. All lines are read when num of lines in file is less than N.
* Last line may be a incomplete, so discard it. Modify curPos appropriately.
* 9 Exit. Got N lines or less than that.
*
* @param fileName file name
* @param lineCount number of last lines
* @param chunkSize chunk size
* @return array of lines
*/
public static ArrayList<String> tail(String fileName, int lineCount, int chunkSize) {
try {
RandomAccessFile raf = new RandomAccessFile(fileName, "r");
ArrayList<String> lastNlines = new ArrayList<String>();
int delta = 0;
long curPos = raf.length() - 1;
if (curPos < 0){
return lastNlines;
}
long fromPos;
byte[] bytearray;
while (true) {
fromPos = curPos - chunkSize;
//System.out.println(curPos);
//System.out.println(fromPos);
if (fromPos <= 0) {
raf.seek(0);
bytearray = new byte[(int) curPos];
raf.readFully(bytearray);
parseLinesFromLast(bytearray, lineCount, lastNlines);
break;
} else {
raf.seek(fromPos);
bytearray = new byte[chunkSize];
raf.readFully(bytearray);
if (parseLinesFromLast(bytearray, lineCount, lastNlines)) {
break;
}
delta = lastNlines.get(lastNlines.size() - 1).length();
lastNlines.remove(lastNlines.size()-1);
curPos = fromPos + delta;
}
}
return lastNlines;
} catch(Exception e) {
e.printStackTrace();
return null;
}
}
public static String getEscapedPath(String filePath, String separator) {
if ("".equals(filePath.trim())) {
return filePath;
}
String[] array = filePath.split("\\"+separator);
StringBuilder sb = new StringBuilder();
for (int i=0, size=array.length; i<size; i++) {
sb.append(array[i]);
if (i < size-1) {
sb.append("\\");
sb.append(File.separator);
}
}
return sb.toString();
}
public static void copyTemplateToClasspath(Report report) throws IOException {
copyTemplate(report, new File(ExportAction.REPORTS_DIR));
}
public static void copyTemplate(Report report, File directory) throws IOException {
ReportLayout layout = LayoutHelper.getReportLayout();
if (report != null) {
// run report from tree (without open)
layout = report.getLayout();
}
String templateName = layout.getTemplateName();
if ((templateName != null) && !"".equals(templateName.trim())) {
String fromPath = Globals.getCurrentReportAbsolutePath();
if (fromPath == null) {
fromPath = Globals.getTreeReportAbsolutePath();
}
FileUtil.copyToDir(new File(new File(fromPath).getParentFile().getAbsolutePath() + File.separator + templateName),
directory, true);
}
}
public static void copyImagesToClasspath(Report report) throws IOException {
copyImages(report, new File(ExportAction.REPORTS_DIR));
}
public static void copyImages(Report report, File directory) throws IOException {
ReportLayout layout = LayoutHelper.getReportLayout();
if (report != null) {
// run report from tree (without open)
layout = report.getLayout();
}
List<Band> bands = layout.getBands();
for (Band band : bands) {
for (int i=0, rows = band.getRowCount(); i<rows; i++) {
List<BandElement> list = band.getRow(i);
for (BandElement be : list) {
if ((be instanceof ImageBandElement) && !(be instanceof ChartBandElement) && !(be instanceof BarcodeBandElement)) {
String image = ((ImageBandElement)be).getImage();
if ((image == null) || "".equals(image.trim())) {
continue;
}
String fromPath = Globals.getCurrentReportAbsolutePath();
if (fromPath == null) {
fromPath = Globals.getTreeReportAbsolutePath();
}
// catch exception here to allow other found images to be copied
try {
FileUtil.copyToDir(
new File(new File(fromPath).getParentFile().getAbsolutePath() + File.separator + image ),
directory, true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
}
if (layout.getBackgroundImage() != null) {
String fromPath = Globals.getCurrentReportAbsolutePath();
if (fromPath == null) {
fromPath = Globals.getTreeReportAbsolutePath();
}
try {
FileUtil.copyToDir(
new File(new File(fromPath).getParentFile().getAbsolutePath() + File.separator + layout.getBackgroundImage() ),
directory, true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
public static void copyImages(File reportFile, File directory) {
try {
Report report = ReportUtil.loadReport(new FileInputStream(reportFile));
List<String> images = ReportUtil.getStaticImages(report);
String prefix = reportFile.getParentFile().getAbsolutePath();
for (String image : images) {
File f = new File(prefix + File.separator + image);
if (f.exists()) {
FileUtil.copyToDir(f, directory);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void deleteImages(Report report) {
ReportLayout layout = LayoutHelper.getReportLayout();
if (report != null) {
// run report from tree (without open)
layout = report.getLayout();
}
List<Band> bands = layout.getBands();
for (Band band : bands) {
for (int i=0, rows = band.getRowCount(); i<rows; i++) {
List<BandElement> list = band.getRow(i);
for (BandElement be : list) {
if (be instanceof ImageBandElement) {
String image = ((ImageBandElement)be).getImage();
deleteImage(image);
}
}
}
}
if (layout.getBackgroundImage() != null) {
deleteImage(layout.getBackgroundImage());
}
}
public static void deleteImage(String name) {
String fromPath = Globals.getCurrentReportAbsolutePath();
if (fromPath == null) {
fromPath = Globals.getTreeReportAbsolutePath();
}
//System.out.println("*** path="+new File(fromPath).getParentFile().getAbsolutePath() + File.separator + name);
new File(new File(fromPath).getParentFile().getAbsolutePath() + File.separator + name).delete();
}
public static void createFile(String filePath, byte[] content) throws IOException {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(filePath);
fos.write(content);
} finally {
if (fos != null) {
fos.close();
}
}
}
public static void openFile(final String fileName, Class classFile) {
Runnable openFileRunnable = new Runnable() {
public void run() {
String name = "";
try {
name = new File(fileName).getCanonicalPath();
Desktop.open(new File(name));
} catch (Throwable t) {
// jdic is built for 32 bit platforms and an error will rise for 64 bit platforms
// try to open with java Desktop
try {
java.awt.Desktop.getDesktop().open(new File(name));
} catch (Throwable t1) {
// java Desktop is buggy on some JRE's
// we can try on Windows to use the following
boolean ok = false;
if (System.getProperty("os.name").startsWith("Windows")) {
try {
Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + name);
ok = true;
} catch (Throwable t2) {
ok = false;
}
}
if (!ok) {
Show.error(I18NSupport.getString("file.open.error", name)+ "\n" + t1.getMessage());
}
}
}
}
};
new Thread(openFileRunnable, "NEXT : " + classFile.getSimpleName()).start();
}
public static void openUrl(final String url, Class classFile) {
Runnable openFileRunnable = new Runnable() {
public void run() {
try {
Desktop.browse(new URL(url));
} catch (Throwable t) {
// jdic is built for 32 bit platforms and an error will rise for 64 bit platforms
// try to open with java Desktop
try {
java.awt.Desktop.getDesktop().browse(new URI(new URL(url).toString()));
} catch (Throwable t1) {
Show.error(I18NSupport.getString("url.open.error", url)+ "\n" + t.getMessage());
}
}
}
};
new Thread(openFileRunnable, "NEXT : " + classFile.getSimpleName()).start();
}
public static String convertPathToSystemSeparators(String p) {
if (p == null) {
return null;
}
p = p.replaceAll("\\\\", "/");
String[] array = p.split("/");;
StringBuilder sb = new StringBuilder();
for (int i=0, size=array.length; i<size; i++) {
if ("".equals(array[i])) {
continue;
}
sb.append(array[i]);
if (i < size-1) {
sb.append(File.separator);
}
}
return sb.toString();
}
public static String readFileAsString(String filePath) throws IOException {
StringBuffer fileData = new StringBuffer(1000);
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(filePath));
char[] buf = new char[1024];
int numRead = 0;
while ((numRead = reader.read(buf)) != -1) {
String readData = String.valueOf(buf, 0, numRead);
fileData.append(readData);
buf = new char[1024];
}
return fileData.toString();
} finally {
if (reader != null) {
reader.close();
}
}
}
public static String getBaseFileName(String path) {
File file = new File(path);
String name = file.getName();
int index = name.lastIndexOf(".");
if (index == -1) {
return name;
} else {
return name.substring(0,index);
}
}
}