/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Contributor(s):
*
* The Original Software is NetBeans. The Initial Developer of the Original
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
* Microsystems, Inc. All Rights Reserved.
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*/
package org.netbeans.microedition.lcdui.pda;
import java.util.*;
import java.io.*;
import javax.microedition.io.*;
import javax.microedition.io.file.*;
import javax.microedition.lcdui.*;
import org.albite.albite.AlbiteMIDlet;
import org.albite.book.model.book.Book;
//#if !((defined(LightMode) || defined(TinyMode) || defined(LightModeExport) || defined(TinyModeExport)))
import org.albite.lang.AlbiteCharacter;
//#endif
/**
* The <code>FileBrowser</code> custom component lets the user list files and
* directories. It's uses FileConnection Optional Package (JSR 75). The FileConnection
* Optional Package APIs give J2ME devices access to file systems residing on mobile devices,
* primarily access to removable storage media such as external memory cards.
* @author breh
*/
public class FileBrowser extends List implements CommandListener {
/**
* Command fired on file selection.
*/
public static final Command SELECT_FILE_COMMAND = new Command("Select", Command.OK, 1);
private String currDirName;
private String currFile;
private Image dirIcon;
private Image fileIcon;
private Image[] iconList;
private CommandListener commandListener;
/* special string denotes upper directory */
private static final String UP_DIRECTORY = "..";
/* special string that denotes upper directory accessible by this browser.
* this virtual directory contains all roots.
*/
private static final String MEGA_ROOT = "/";
/* separator string as defined by FC specification */
private static final String SEP_STR = "/";
/* separator character as defined by FC specification */
private static final char SEP = '/';
private Display display;
private String selectedURL;
private String filter = null;
private String title;
/**
* Creates a new instance of FileBrowser for given <code>Display</code> object.
* @param display non null display object.
*/
public FileBrowser(final Display display) {
super("", IMPLICIT);
currDirName = MEGA_ROOT;
this.display = display;
super.setCommandListener(this);
setSelectCommand(SELECT_FILE_COMMAND);
try {
dirIcon = Image.createImage("/org/netbeans/microedition/resources/dir.png");
} catch (IOException e) {
dirIcon = null;
}
try {
fileIcon = Image.createImage("/org/netbeans/microedition/resources/file.png");
} catch (IOException e) {
fileIcon = null;
}
iconList = new Image[]{fileIcon, dirIcon};
showDir();
}
/**
* Sets up an initial directory, i.e. possibly different from MEGA_ROOT
*/
public void setDir(final String dir) {
if (dir != null) {
try {
FileConnection currDir =
(FileConnection) Connector.open(
"file:///" + dir, Connector.READ);
try {
if (currDir.exists() && currDir.isDirectory()) {
currDirName = dir;
showDir();
}
} finally {
currDir.close();
}
} catch (IOException ioe) {
//#debug
AlbiteMIDlet.LOGGER.log(ioe);
}
}
}
private void showDir() {
// new Thread(new Runnable() {
// public void run() {
try {
showCurrDir();
} catch (SecurityException e) {
Alert alert = new Alert("Error", "You are not authorized to access the restricted API", null, AlertType.ERROR);
alert.setTimeout(2000);
display.setCurrent(alert, FileBrowser.this);
} catch (Exception e) {
//#debug
AlbiteMIDlet.LOGGER.log(e);
}
// }
// }).start();
}
/**
* Indicates that a command event has occurred on Displayable d.
* @param c a <code>Command</code> object identifying the command. This is either
* one of the applications have been added to <code>Displayable</code> with <code>addCommand(Command)</code>
* or is the implicit <code>SELECT_COMMAND</code> of List.
* @param d the <code>Displayable</code> on which this event has occurred
*/
public final void commandAction(final Command c, final Displayable d) {
if (c.equals(SELECT_FILE_COMMAND)) {
List curr = (List) d;
currFile = curr.getString(curr.getSelectedIndex());
// new Thread(new Runnable() {
//
// public void run() {
if (currFile.endsWith(SEP_STR) || currFile.equals(UP_DIRECTORY)) {
openDir(currFile);
} else {
//switch To Next
doDismiss();
}
// }
// }).start();
} else {
commandListener.commandAction(c, d);
}
}
/**
* Sets component's title.
* @param title component's title.
*/
public final void setTitle(final String title) {
this.title = title;
super.setTitle(title);
}
/**
* Show file list in the current directory .
*/
private void showCurrDir() {
if (title == null) {
super.setTitle(currDirName);
}
Enumeration e = null;
FileConnection currDir = null;
deleteAll();
if (MEGA_ROOT.equals(currDirName)) {
// append(UP_DIRECTORY, dirIcon);
e = FileSystemRegistry.listRoots();
} else {
try {
currDir =
(FileConnection) Connector.open(
"file:///" + currDirName, Connector.READ);
e = currDir.list();
} catch (IOException ioe) {
//#debug
AlbiteMIDlet.LOGGER.log(ioe);
}
append(UP_DIRECTORY, dirIcon);
}
if (e == null) {
try {
currDir.close();
} catch (IOException ioe) {
//#debug
AlbiteMIDlet.LOGGER.log(ioe);
}
return;
}
final Vector directoriesVector = new Vector();
final Vector filesVector = new Vector();
while (e.hasMoreElements()) {
String fileName = (String) e.nextElement();
if (fileName.charAt(fileName.length() - 1) == SEP) {
// This is directory
directoriesVector.addElement(fileName);
} else {
// this is regular file
boolean append = false;
final String fileNameLW = fileName.toLowerCase();
for (int i = 0; i < Book.SUPPORTED_BOOK_EXTENSIONS.length; i++){
if (fileNameLW.endsWith(Book.SUPPORTED_BOOK_EXTENSIONS[i])) {
append = true;
break;
}
}
if (append) {
filesVector.addElement(fileName);
}
}
}
if (!directoriesVector.isEmpty()) {
final String[] directories = new String[directoriesVector.size()];
directoriesVector.copyInto(directories);
sortStringArray(directories);
for (int i = 0; i < directories.length; i++) {
append(directories[i], dirIcon);
}
}
if (!filesVector.isEmpty()) {
final String[] files = new String[filesVector.size()];
filesVector.copyInto(files);
sortStringArray(files);
for (int i = 0; i < files.length; i++) {
append(files[i], fileIcon);
}
}
if (currDir != null) {
try {
currDir.close();
} catch (IOException ioe) {
//#debug
AlbiteMIDlet.LOGGER.log(ioe);
}
}
}
private void openDir(final String fileName) {
/* In case of directory just change the current directory
* and show it
*/
if (currDirName.equals(MEGA_ROOT)) {
if (fileName.equals(UP_DIRECTORY)) {
// can not go up from MEGA_ROOT
return;
}
currDirName = fileName;
} else if (fileName.equals(UP_DIRECTORY)) {
// Go up one directory
// TODO use setFileConnection when implemented
int i = currDirName.lastIndexOf(SEP, currDirName.length() - 2);
if (i != -1) {
currDirName = currDirName.substring(0, i + 1);
} else {
currDirName = MEGA_ROOT;
}
} else {
currDirName += fileName;
}
showDir();
}
/**
* Returns selected file as a <code>FileConnection</code> object.
* @return non null <code>FileConection</code> object
*/
public final FileConnection getSelectedFile() throws IOException {
FileConnection fileConnection =
(FileConnection) Connector.open(selectedURL);
return fileConnection;
}
/**
* Returns selected <code>FileURL</code> object.
* @return non null <code>FileURL</code> object
*/
public final String getSelectedFileURL() {
return selectedURL;
}
/**
* Sets the file filter.
* @param filter file filter String object
*/
public final void setFilter(final String filter) {
this.filter = filter;
}
/**
* Returns command listener.
* @return non null <code>CommandListener</code> object
*/
protected final CommandListener getCommandListener() {
return commandListener;
}
/**
* Sets command listener to this component.
* @param commandListener <code>CommandListener</code> to be used
*/
public final void setCommandListener(
final CommandListener commandListener) {
this.commandListener = commandListener;
}
private void doDismiss() {
selectedURL = "file:///" + currDirName + currFile;
CommandListener commandListener = getCommandListener();
if (commandListener != null) {
commandListener.commandAction(SELECT_FILE_COMMAND, this);
}
}
protected static void sortStringArray(final String[] strings) {
/*
* If one wants useful results one should be comparing lowercase letters
*/
final String[] lowercaseStrings = new String[strings.length];
for (int i = 0; i < strings.length; i++) {
//#if (defined(LightMode) || defined(TinyMode) || defined(LightModeExport) || defined(TinyModeExport))
//# lowercaseStrings[i] = strings[i].toLowerCase();
//#else
lowercaseStrings[i] = AlbiteCharacter.toLowerCase(strings[i]);
//#endif
}
int n = strings.length;
String temp;
for(int i = 0; i < n; i++){
for(int j = 1; j < (n-i); j++){
if(lowercaseStrings[j - 1].compareTo(lowercaseStrings[j]) > 0 ){
//swap the elements!
temp = strings[j - 1];
strings[j - 1] = strings[j];
strings[j] = temp;
temp = lowercaseStrings[j - 1];
lowercaseStrings[j - 1] = lowercaseStrings[j];
lowercaseStrings[j] = temp;
}
}
}
}
// protected static void sortStringArray(final String[] strings) {
//
// int newLowest = 0; // index of first comparison
// int newHighest = strings.length-1; // index of last comparison
//
// while (newLowest < newHighest) {
// int highest = newHighest;
// int lowest = newLowest;
// newLowest = strings.length; // start higher than any legal index
// for (int i=lowest; i<highest; i++) {
// if (strings[i].compareTo(strings[i + 1]) > 0) {
// // exchange elements
// String temp = strings[i];
// strings[i] = strings[i+1];
// strings[i+1] = temp;
// if (i<newLowest) {
// newLowest = i-1;
// if (newLowest < 0) {
// newLowest = 0;
// }
// } else if (i>newHighest) {
// newHighest = i+1;
// }
// }
// }
// }
// }
}