package cologne.eck.all_peas.files;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import cologne.eck.all_peas.control.PathFileManager;
import cologne.eck.all_peas.data.Attachments;
import cologne.eck.all_peas.data.PeaProperties;
import cologne.eck.all_peas.gui.PeaDialog;
import cologne.eck.all_peas.gui.PeaFileChooser;
import cologne.eck.tools.FileTools;
import settings.PeaSettings;
public class FileComposer {
/**
* the associated FileModel with this instance of FileComposer
*/
private FileModel fileModel;
/**
* The FileTypePanel associated with this instance of FileComposer.
* This is usedfor the progress bar
*/
private FileTypePanel ftp;
/*
* true if the shown files are plain text
* false if the shown files are encrypted
*/
private boolean plainModus = true;
/*
* For test mode: print all exceptions and warnings
*/
private static boolean printErrors;
/*
* true if a warning (directories includes all sub-directories) was already shown
* (this warning should appear at most once for each session)
*/
private static boolean directoryWarning = false;
/*
* true: double encryption (encryption of encrypted files) is allowed,
* false: do not add encrypted files in plain modus
*/
private boolean doubleEncrypt = false;
private boolean doubleEncryptDecided = false;
/*
* FileChooser associated with this instance to choose new file
*/
private PeaFileChooser chooser;
/*
* last selected file. Used to open FileChooser at this directory
*/
private static File lastDir = null;
public FileComposer(FileModel _fileModel){
fileModel = _fileModel;
if (PeaProperties.getWorkingMode().equals("-t")){
printErrors = true;
}
}
/**
* Sort an array of files and update
* the check box panel. The method always checks if
* a directory contains a valid file and sets it
* possibly invalid.
*
* @param checkAgain true if already listed files
* should be checked again
*/
public final void updateFiles(boolean checkAgain){
File[] allFileNamesFromPanel = fileModel.getAllFiles(); {//ftp.getAllFileNames();
//for(int i=0;i<fileModel.getAllFiles().length;i++) System.out.println(fileModel.getAllFiles()[i].getAbsolutePath());
// append "-" as start of annotation
ftp.markUnselectedValidFiles();
if (allFileNamesFromPanel == null) {
return;
}
int len = allFileNamesFromPanel.length;
if (len == 0) {
return;
}
String invalidMarker = FileModel.getInvalidMarker();
// clear the panel
ftp.removeAllCheckBoxes();
if (checkAgain == false) {
for (int i = 0; i < len; i++){
File file = allFileNamesFromPanel[i];
String fileName = file.getAbsolutePath();
String annotation = fileModel.getAnnotation(file);
if (file.isFile()) {
if (annotation.contains(invalidMarker)){
ftp.addInvalidFileCheckBox(fileName, annotation);
} else {
ftp.addValidFileCheckBox(fileName, annotation);
}
} else if (file.isDirectory()){
if (annotation.contains(invalidMarker)){
ftp.addDirectoryCheckBox(fileName, annotation, false);// valid: false
} else {
// check if directory contains valid file:
if (checkContainingChildren(file, i, allFileNamesFromPanel) == false){
// directory does not contain a valid file: mark as invalid
String newAnnotation = PeaProperties.getBundle().getString("no_valid_files_inside")
+ invalidMarker;
// update the map:
fileModel.setAnnotation(file, newAnnotation);
// set check box:
ftp.addDirectoryCheckBox(fileName, annotation, false);
} else { // valid file inside
ftp.addDirectoryCheckBox(fileName, annotation, true);
}
}
} else {
ftp.addInvalidFileCheckBox(fileName, annotation);
}
}
} else { // check all files again:
// remove all mappings from TreeMap
fileModel.resetTreeMap();
fileModel.setAllSize(0);
fileModel.setFileNumber(0);
// get list of file names:
String[] allFileNames = new String[len];
for (int i = 0; i < len; i++) {
allFileNames[i] = allFileNamesFromPanel[i].getAbsolutePath();
}
ArrayList<String> list = new ArrayList<String>(Arrays.asList(allFileNames));
// check all files, but do not add check boxes
addFilesToMap(list, false, false, false);
// check for empty files and add check boxes
updateFiles(false);// do not check already listed files again
ftp.updateWindow();
}
}
ftp.updateNumberAndSize();
}
/**
* Check if a directory contains at least
* one valid file (not sub-directory) or not.
*
* @param folder the folder to check
* @param indexInArray the index of the folder to check in the
file names array
* @param files an array of file names to check if
* an child of folder is included
*
* @return true, if the folder is valid and not empty
* false otherwise
*/
private boolean checkContainingChildren(
File folder, int indexInArray, File[] files){
String invalidMarker = FileModel.getInvalidMarker();
int size = files.length;
for (int i = indexInArray + 1; i < size; i++) { // start with element after file
File nextFile = files[i];
if (nextFile.isDirectory()){
// check recursive
if (nextFile.getParentFile().equals(folder)){
if (checkContainingChildren(nextFile, i, files) == true){
return true;
}
}
} else { // file
if (nextFile.getParentFile().equals(folder)){
if (! fileModel.getAnnotation(nextFile).contains(invalidMarker)){
return true;
}
}
}
}
return false;
}
/**
* Create Strings for file names to display the
* file hierarchy.
*
* @param allFileNames the files names to display
*
* @return an array of these file names
* with hierarchy view
*/
public final String[] showFileHierarchy(String[] allFileNames){
if (allFileNames == null) {
return null;
}
int len = allFileNames.length;
// result
ArrayList<String> resultList = new ArrayList<String>(len);
// marker to display the file hierarchy:
String marker = " . . "; // ��� ��� ��� ���
ArrayList<File> parents = new ArrayList<File>();
for (int i = 0; i < len; i++) {
File file= new File(allFileNames[i]);
String fileNameWithPath = file.getAbsolutePath();
File parent = file.getParentFile();
while (parents.isEmpty() == false) {// stop if there is no parent
// check if the last added parent is parent of this file:
int lastIndex = parents.size() - 1;
if (parent.equals(parents.get(lastIndex))){
// create the space string for hierarchy view
String space = new String(new char[lastIndex]).replace("\0", " ");
// add space and replace parent with marker
// escape \ with \\ to avoid java.util.regex.PatternSyntaxException in Windows
// fileNameWithPath = space + fileNameWithPath.replaceFirst(
// parents.get(lastIndex).getAbsolutePath(), marker);
fileNameWithPath = space + marker +
fileNameWithPath.substring(
parent.getAbsolutePath().length(),
fileNameWithPath.length());
// resulting string is ready
break;
} else {
// list is sorted, last parent is not parent anymore
parents.remove(lastIndex);
}
}
// add the maybe modified string
resultList.add(fileNameWithPath);
if (file.isDirectory()) {
// to check the next file
parents.add(file);
}
}
return resultList.toArray(new String[resultList.size()]);
}
/**
* Display the previously stored file names
* and warn for long execution time if these files are too large
* or too many.
*/
public final void initFileNames() {
if (Attachments.getFileIdentifier() == null) { // needs to check
Attachments.setFileIdentifier(PeaSettings.getFileIdentifier() );
}
// check EXTERNAL_FILE_PATH:
if (PeaSettings.getExternalFilePath() != null) {
// checks existence, fileIdentifier...:
addFileToMap(PeaSettings.getExternalFilePath(), true, false, false);
}
// check path file:
/* String PATH_FILE_NAME = PeaSettings.getJarFileName() + ".path";
File file = new File(PATH_FILE_NAME);
if (! file.exists() ) {
//System.err.println("FileDisplayPanel: no path file specified");
return;
}
if (! file.canRead() ) {
System.err.println("FileDisplayPanel: can not read path file " + file.getName() );
PeaDialog.showMessage(ftp, //message, title, messageType);.showInternalMessageDialog(PswDialogView.getView(),
"There is are file containing names of potentially encrypted files,\n" +
" but no access to it: \n" + PATH_FILE_NAME,
"Info",//title
1); // Information message type
return;
}
if ( file.length() == 0){
if (PeaProperties.getWorkingMode().equals("-t")){
System.err.println("FileDisplayPanel: empty path file " + file.getName() );
}
return;
}
byte[] pathBytes = ReadResources.readExternFile( file.getName() );
String pathString = null;
try {
pathString = new String(pathBytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
System.err.println("FileTypePanel: " + e);
if (PeaProperties.getWorkingMode().equals("-t")){
e.printStackTrace();
}
}*/
String[] pathNames = PathFileManager.accessPathFile();// pathString.split("\n");
if (pathNames == null) {
if (PeaProperties.getWorkingMode().equals("-t")){
System.err.println("FileDisplayPanel: empty path file. " );
}
return;
} else {
// show only valid files:
// there are no file names, so just sort
ArrayList<String> list = new ArrayList<String>(Arrays.asList(pathNames));
Collections.sort(list);
pathNames = list.toArray(new String[list.size()]);
ArrayList<File> directories = new ArrayList<File>();
for (int i = 0; i < pathNames.length; i++) {
if (new File(pathNames[i]).isDirectory()){
directories.add(new File(pathNames[i]));
}
if (PathFileManager.checkFile(pathNames[i], false) == null) {// checkFile, return null if valid
// check expected execution time and show warning if long
if (ExecutionTimeObserver.warnExecutionTime(
1,
FileTools.getFileSize(pathNames[i]),
fileModel, ftp) == true ){
addFileToMap(pathNames[i], true, false, false);
} else { // canceled or closed
// if this file was checked and added
fileModel.remove(new File(pathNames[i]));
}
}
// TODO test!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// if (PeaControl.checkFile(pathNames[i], false) == null) {// checkFile, return null if valid
/* if (fileModel.checkAndAddFile(new File(pathNames[i]), false, printErrors, true)) {
// check expected execution time and show warning if long
if (ExecutionTimeObserver.warnExecutionTime(
1,
FileTools.getFileSize(pathNames[i]),
fileModel, ftp) == true ){
addFileToMap(pathNames[i], true, false, false);
} else { // canceled or closed
// if this file was checked and added
fileModel.remove(new File(pathNames[i]));
}
} */
}
// Check directoryNames:
// reverse order: parent files should be checked last
Collections.reverse(directories);
int len = directories.size();
for (int i = 0; i < len; i++) {
File dir = directories.get(i);
if (fileModel.hasValidChild(dir)) {
addFileToMap(dir.getAbsolutePath(), false, false, false);
}
}
}
if (fileModel.getFileNumber() > 0){
updateFiles(false);
ftp.updateWindow();
}
//ftp.updateNumberAndSize();
}
/**
* Display a file chooser to add new files
*
* @param message the message to display on top of the FileChooser
* @param hiddenFilesCheckBox true: add a check box to optionally
* show hidden files
* @param checkAgain true: check already listed files again
*
* @return true if at least one file was added
*/
public final boolean addAction(Object window, String message,
boolean hiddenFilesCheckBox, boolean checkAgain) {
chooser = new PeaFileChooser(window);//, locationOnScreen);
if (lastDir != null){
chooser.setCurrentDirectory(lastDir);
}
chooser.setAcceptAllFileFilterUsed(true);
chooser.setFileSelectionMode(PeaFileChooser.FILES_AND_DIRECTORIES);
chooser.setMultiSelectionEnabled(true);
chooser.customizeFileChooser(null, message, true);// no title
int returnVal = chooser.showOpenDialog(window);
if(returnVal == 0) { //FileChooser.APPROVE_OPTION == 0
lastDir = chooser.getCurrentDirectory();
File[] files = chooser.getSelectedFiles();
if (files == null || files.length == 0) {
System.err.println("no files selected");
return false;
}
// check execution time before checking and adding the files:
// this will stop for many or large files with a warning and option to cancel
if (fileModel.checkNumberAndSize(files, ftp, printErrors) == false){
System.err.println("Adding process canceled because of expected execution time");
// do not check or add file
return false;
}
// create new check boxes for selected files
int len = files.length;
String[] selectedFileNames = new String[files.length];
for (int i = 0; i < len; i++) {
selectedFileNames[i] = chooser.getSelectedFiles()[i].getAbsolutePath();
}
ArrayList<String> list = new ArrayList<String>(Arrays.asList(selectedFileNames));
addFilesToMap(list, true, checkAgain, false);// true: include sub-directories
// false: do not check already added files again
// false: do not add check boxes
if (window instanceof FileTypePanel) {
updateFiles(false);// do not check already listed files again
ftp.updateWindow();
//ftp.updateNumberAndSize();
}
return true;
} else { // cancel or close
return false;
}
}
/**
* Get the current number and size of all valid selected files
* in a String
*
* @return Informations about the current number
* and size of all valid selected files as String
*/
public final String getCurrentNumberAndSize() {
if (fileModel.getAllSize() < 0) {
System.err.println("FileDisplayPanel: invalid value for size of selected files (fc.getAllSize()) ");
fileModel.calculateNumberAndSize();
//return ("Error: invalid size");
}
if (fileModel.getFileNumber() < 0) {
System.err.println("FileDisplayPanel: invalid value for number of selected files (fc.getFileNumber()) ");
fileModel.calculateNumberAndSize();
//return ("Error: invalid number");
}
//System.out.println("number : " + fileModel.getFileNumber());
return (PeaProperties.getBundle().getString("number_of_files")
+ fileModel.getFileNumber()
+ ", " + PeaProperties.getBundle().getString("overall_size")
+ fileModel.getAllSize() + " - (MiB): "
+ fileModel.getAllSize()/1024/1024);
}
/**
* Checks a file (validity, directory or file),
* sets annotations and possibly (addToPanel = true)
* shows a single check box. The
* background color and behavior of the check box
* depends on the validity of the file. For high number
* of file and large file size a warning message appears.
*
* @param fileName the name of the file to show
* @param includeChildren true, if children of directories should be
* included (for initialization case), false if not
* (all other cases)
* @param checkAgain true if already listed files should be checked again
* @param addToPanel true: add check box to panel, false: only set annotations
*/
public final void addFileToMap(String fileName,
boolean includeChildren,
boolean checkAgain,
boolean addToPanel) {
File file = new File(fileName);
boolean markAsEncrypted = false;
// check if plain files are encrypted
if (plainModus == true) {
if (doubleEncrypt == false) { // do not encrypt encrypted file
// check file identifier (true for already encrypted files)
if (file.isDirectory() == false) {
if (fileModel.checkFileIdentifier(file, printErrors, false) == true ) {
//System.out.println(file.getAbsolutePath());
if (doubleEncryptDecided == false) { // not asked before
// this is an encrypted file, display warning and option to break
int result = PeaDialog.showQuestion(ftp,
PeaProperties.getBundle().getString("encrypt_encrypted_files"),
PeaProperties.getBundle().getString("warning"), // title
0, // YES_NO_OPTION
2); // WARNING_MESSAGE
if (result == 0) { // YES_OPTION
doubleEncryptDecided = true; // do not ask again
doubleEncrypt = true; // always add encrypted files
} else if (result == 1){ // NO_OPTION
doubleEncryptDecided = true;
doubleEncrypt = false;
markAsEncrypted = true;
} else { // close
return;
}
} else { //already decided: do not add encrypted files
markAsEncrypted = true;
}
} else { // no file identifier
// nothing to do
}
}
} else { // doubleEncrypt true
// nothing to to do, don't check, just encrypt again
}
} else {// encrypted Modus
// no extra check for encrypted files
}
if (fileModel.checkAndAddFile(file, plainModus, printErrors, checkAgain) == false) {
// file was not added
if (checkAgain == false) {
return;
}
}
if (markAsEncrypted == true) {
// set annotation
fileModel.setAnnotation(
file, PeaProperties.getBundle().getString("already_encrypted")
+ FileModel.getInvalidMarker());
}
String annotation = fileModel.getAnnotation(file);
if (file.isFile()) {
if (annotation.contains(FileModel.getInvalidMarker())){
if (addToPanel == true) {
ftp.addInvalidFileCheckBox(fileName, annotation);
}
} else {
// update size and number:
long fileSize = FileTools.getFileSize(fileName);
fileModel.setAllSize(fileModel.getAllSize() + fileSize);
fileModel.setFileNumber(fileModel.getFileNumber() + 1);
if (addToPanel == true) {
ftp.addValidFileCheckBox(fileName, annotation);
}
}
} else if (file.isDirectory()){
if (annotation.contains(FileModel.getInvalidMarker())){
if (addToPanel == true) {
ftp.addDirectoryCheckBox(fileName, annotation, false);// valid: false
}
} else { // valid directory
if (includeChildren == true){ // adding new files
//check if at least one child file is valid:
// this will also test execution time and show warning
int checkChildren = fileModel.checkAndAddIncludedChildren(file, printErrors, plainModus, ftp);
if (checkChildren == 0) { // valid file found, not cancelled
// warning for directory: once for each session
// directory contains all sub-directories and (hidden) files...
if (plainModus == true && directoryWarning == false ){
directoryWarning = true;
PeaDialog.showMessage(
ftp,
PeaProperties.getBundle().getString("adding_directory_includes_hidden_files"),
PeaProperties.getBundle().getString("warning"),
2); // JOptionPane.WARNING_MESSAGE
}
if (addToPanel == true) {
ftp.addDirectoryCheckBox(fileName, annotation, true);
}
// get all children
String[] includedFileNames = FileTools.getAllFileNamesOfFolder(fileName, false);
ArrayList<String> list = new ArrayList<String>(Arrays.asList(includedFileNames));
// add all children
addFilesToMap(list, includeChildren, checkAgain, addToPanel);
} else if (checkChildren == 1){ // no valid file found, not cancelled
// set annotation
String newAnnotation =
PeaProperties.getBundle().getString("no_valid_files_inside")
+ FileModel.getInvalidMarker();
// add invalid check box
if (addToPanel == true) {
ftp.addDirectoryCheckBox(fileName, newAnnotation, false);//, nextIndex++);
}
} else if (checkChildren == -1){ // canceled because of warning
System.err.println("adding file cancelled");
// remove file from map:
fileModel.remove(file);
}
} else { // called by sortFiles: do not include children
// validity (not empty) is already checked
if (addToPanel == true) {
ftp.addDirectoryCheckBox(fileName, annotation, true);
}
}
}
}
}
/**
* Displays selected files as check boxes, checks files, displays warning,
* updates the TreeMap if required and
* adds comments for invalid files.
*
* @param newFileNames the new selected files
* @param includeSubdirectories true: include all sub-directories if the
* chosen files
* @param checkAgain true if already listed files should be
* checked again
* @param addToPanel true: add check box for files,
* false: only set annotations
*/
public final void addFilesToMap(ArrayList<String> newFileNames,
boolean includeSubdirectories, boolean checkAgain, boolean addToPanel) {
// sort the file names
Collections.sort(newFileNames);
// get the index of the first file:
int len = newFileNames.size();
for (int i = 0; i < len; i++) {
String fileName = newFileNames.get(i);
addFileToMap(fileName, includeSubdirectories, checkAgain, addToPanel);
} // end for loop over file names
ftp.updateFileView();
}
/**
* Set a new FileModel with a new TreeMap and display the
* new entries.The validity must be checked before
* (annotations must be set before).
*
* @param newModel the new FileModel to set and to display
*/
/* public final void displayNewModel(FileModel newModel){
this.fileModel = newModel;
TreeMap<File, String> newMap = fileModel.getTreeMap();
String invalidMarker = FileModel.getInvalidMarker();
for(Map.Entry<File, String> entry : newMap.entrySet()) {
String annotation = entry.getValue();
File file = entry.getKey();
boolean valid = false;
if (annotation.contains(invalidMarker)){
valid = false;
} else {
valid = true;
}
if (file.isFile()){
if (valid == false) {
ftp.addInvalidFileCheckBox(file.getAbsolutePath(), annotation);
} else {
ftp.addValidFileCheckBox(file.getAbsolutePath(), annotation);
}
} else if (file.isDirectory()){
ftp.addDirectoryCheckBox(file.getAbsolutePath(), annotation,
valid);
} else { // neither file nor directory
ftp.addInvalidFileCheckBox(file.getAbsolutePath(), annotation);
}
}
ftp.updateNumberAndSize();
} */
//================== Getter & Setter =====================
/**
* Get the PeaFileChooser associated with this FileComposer.
* The PeaFileChooser stores the last selected directory.
*
* @return the PeaFileChooser
*/
public PeaFileChooser getPeaFileChooser() {
return chooser;
}
/**
* Get the FileModel which is associated
* with this instance of FileComposer
*
* @return the fileModel
*/
public FileModel getFileModel() {
return fileModel;
}
/**
* Set the FileModel which is associated
* with this instance of FileComposer
*
* @param fileModel the fileModel to set
*/
public void setFileModel(FileModel fileModel) {
this.fileModel = fileModel;
}
/**
* Get the current modus: plain text files
* or encrypted files
*
* @return true if currently processed files
* are plain text files,
* false if these files are encrypted
*/
public final boolean getPlainModus() {
return plainModus;
}
/**
* Set the current modus: plain text files
* or encrypted files
*
* @param true to indicate that currently
* processed files should be treated
* as plain text files,
* false if these files should be
* treated as encrypted files
*/
public final void setPlainModus(boolean _modus) {
plainModus = _modus;
}
/**
* @return the ftp
*/
public FileTypePanel getFileTypePanel() {
return ftp;
}
/**
* @param ftp the ftp to set
*/
public void setFileTypePanel(FileTypePanel ftp) {
this.ftp = ftp;
}
/**
* Print error messages (text mode) or not.
*
* @return true for print error messages
*/
public boolean isPrintErrors(){
return printErrors;
}
}