Commit 183dcb18 authored by Jeffrey I. Schiller's avatar Jeffrey I. Schiller

Ensure asset names are unique on a case insenstive file system.

Change-Id: Idd7c4d8f3dc0bbb1e3ddaa1dcd49693d37f2e7f9
parent a433e3ab
......@@ -905,10 +905,10 @@ public interface OdeMessages extends Messages {
@Description("Error message reported when a file was not selected.")
String noFileSelected();
@DefaultMessage("A file named {0} already exists in this project. Do you want to overwrite " +
"the old file?")
@Description("Confirmation message shown when a file is about to be overwritten.")
String confirmOverwrite(String filename);
@DefaultMessage("A file named {0} already exists in this project. Do you want to remove " +
"this old file? This will also remove any other files whose names conflict with {1}.")
@Description("Confirmation message shown when conflicting files are about to be deleted.")
String confirmOverwrite(String newFile, String existingFile);
// Used in wizards/KeystoreUploadWizard.java
......
......@@ -4,6 +4,8 @@ package com.google.appinventor.client.wizards;
import static com.google.appinventor.client.Ode.MESSAGES;
import java.io.File;
import com.google.appinventor.client.ErrorReporter;
import com.google.appinventor.client.Ode;
import com.google.appinventor.client.OdeAsyncCallback;
......@@ -27,6 +29,7 @@ import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FileUpload;
import com.google.gwt.user.client.ui.VerticalPanel;
/**
* Wizard for uploading individual files.
*
......@@ -87,12 +90,32 @@ public class FileUploadWizard extends Wizard {
Window.alert(MESSAGES.filenameBadSize());
return;
}
if (fileAlreadyExists(folderNode, filename)) {
if (!confirmOverwrite(folderNode, filename)) {
return;
String fn = conflictingExistingFile(folderNode, filename);
if (fn != null && !confirmOverwrite(folderNode, fn, filename)) {
return;
} else {
String fileId = folderNode.getFileId() + "/" + filename;
// We delete all the conflicting files.
for (ProjectNode child : folderNode.getChildren()) {
if (fileId.equalsIgnoreCase(child.getFileId()) && !fileId.equals(child.getFileId())) {
final ProjectNode node = child;
Ode ode = Ode.getInstance();
ode.getEditorManager().closeFileEditor(node.getProjectId(), node.getFileId());
ode.getProjectService().deleteFile(
node.getProjectId(), node.getFileId(),
new OdeAsyncCallback<Long>(
// message on failure
MESSAGES.deleteFileError()) {
@Override
public void onSuccess(Long date) {
Ode.getInstance().getProjectManager().getProject(node).deleteNode(node);
Ode.getInstance().updateModificationDate(node.getProjectId(), date);
}
});
}
}
}
ErrorReporter.reportInfo(MESSAGES.fileUploadingMessage(filename));
// Use the folderNode's project id and file id in the upload URL so that the file is
......@@ -102,25 +125,25 @@ public class FileUploadWizard extends Wizard {
folderNode.getFileId() + "/" + filename;
Uploader.getInstance().upload(upload, uploadUrl,
new OdeAsyncCallback<UploadResponse>(MESSAGES.fileUploadError()) {
@Override
public void onSuccess(UploadResponse uploadResponse) {
switch (uploadResponse.getStatus()) {
case SUCCESS:
ErrorReporter.hide();
onUploadSuccess(folderNode, filename, uploadResponse.getModificationDate(),
fileUploadedCallback);
break;
case FILE_TOO_LARGE:
// The user can resolve the problem by
// uploading a smaller file.
ErrorReporter.reportInfo(MESSAGES.fileTooLargeError());
break;
default:
ErrorReporter.reportError(MESSAGES.fileUploadError());
break;
}
}
});
@Override
public void onSuccess(UploadResponse uploadResponse) {
switch (uploadResponse.getStatus()) {
case SUCCESS:
ErrorReporter.hide();
onUploadSuccess(folderNode, filename, uploadResponse.getModificationDate(),
fileUploadedCallback);
break;
case FILE_TOO_LARGE:
// The user can resolve the problem by
// uploading a smaller file.
ErrorReporter.reportInfo(MESSAGES.fileTooLargeError());
break;
default:
ErrorReporter.reportError(MESSAGES.fileUploadError());
break;
}
}
});
} else {
Window.alert(MESSAGES.noFileSelected());
new FileUploadWizard(folderNode, fileUploadedCallback).show();
......@@ -150,18 +173,24 @@ public class FileUploadWizard extends Wizard {
return filename;
}
private boolean fileAlreadyExists(FolderNode folderNode, String filename) {
private String conflictingExistingFile(FolderNode folderNode, String filename) {
String fileId = folderNode.getFileId() + "/" + filename;
for (ProjectNode child : folderNode.getChildren()) {
if (fileId.equals(child.getFileId())) {
return true;
if (fileId.equalsIgnoreCase(child.getFileId())) {
// we want to return kitty.png rather than assets/kitty.png
return lastPathComponent(child.getFileId());
}
}
return false;
return null;
}
private String lastPathComponent (String path) {
String [] pieces = path.split("/");
return pieces[pieces.length - 1];
}
private boolean confirmOverwrite(FolderNode folderNode, String filename) {
return Window.confirm(MESSAGES.confirmOverwrite(filename));
private boolean confirmOverwrite(FolderNode folderNode, String newFile, String existingFile) {
return Window.confirm(MESSAGES.confirmOverwrite(newFile, existingFile));
}
private void onUploadSuccess(final FolderNode folderNode, final String filename,
......
......@@ -27,6 +27,7 @@ import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
......@@ -116,11 +117,46 @@ public class MediaUtil {
return MediaSource.ASSET;
}
private static String findCaseinsensitivePath(Form form, String mediaPath)
throws IOException{
String[] mediaPathlist = form.getAssets().list("");
int l = Array.getLength(mediaPathlist);
for (int i=0; i<l; i++){
String temp = mediaPathlist[i];
if (temp.equalsIgnoreCase(mediaPath)){
return temp;
}
}
return null;
}
/**
* find path of an asset from a mediaPath using case-insensitive comparison,
* return type InputStream.
* Throws IOException if there is no matching path
* @param form the Form
* @param mediaPath the path to the media
*/
private static InputStream getAssetsIgnoreCaseInputStream(Form form, String mediaPath)
throws IOException{
try {
return form.getAssets().open(mediaPath);
} catch (IOException e) {
if (findCaseinsensitivePath(form, mediaPath) == null){
throw e;
} else {
String path = findCaseinsensitivePath(form, mediaPath);
return form.getAssets().open(path);
}
}
}
private static InputStream openMedia(Form form, String mediaPath, MediaSource mediaSource)
throws IOException {
switch (mediaSource) {
case ASSET:
return form.getAssets().open(mediaPath);
return getAssetsIgnoreCaseInputStream(form,mediaPath);
case REPL_ASSET:
return new FileInputStream(replAssetPath(mediaPath));
......@@ -319,6 +355,28 @@ public class MediaUtil {
// SoundPool related methods
/**
* find path of an asset from a mediaPath using case-insensitive comparison,
* return AssetFileDescriptor of that asset
* Throws IOException if there is no matching path
* @param form the Form
* @param mediaPath the path to the media
*/
private static AssetFileDescriptor getAssetsIgnoreCaseAfd(Form form, String mediaPath)
throws IOException{
try {
return form.getAssets().openFd(mediaPath);
} catch (IOException e) {
if (findCaseinsensitivePath(form, mediaPath) == null){
throw e;
} else {
String path = findCaseinsensitivePath(form, mediaPath);
return form.getAssets().openFd(path);
}
}
}
/**
* Loads the audio specified by mediaPath into the given SoundPool and
* returns the sound id.
......@@ -336,7 +394,7 @@ public class MediaUtil {
MediaSource mediaSource = determineMediaSource(form, mediaPath);
switch (mediaSource) {
case ASSET:
return soundPool.load(form.getAssets().openFd(mediaPath), 1);
return soundPool.load(getAssetsIgnoreCaseAfd(form,mediaPath), 1);
case REPL_ASSET:
return soundPool.load(replAssetPath(mediaPath), 1);
......@@ -374,7 +432,7 @@ public class MediaUtil {
MediaSource mediaSource = determineMediaSource(form, mediaPath);
switch (mediaSource) {
case ASSET:
AssetFileDescriptor afd = form.getAssets().openFd(mediaPath);
AssetFileDescriptor afd = getAssetsIgnoreCaseAfd(form,mediaPath);
try {
FileDescriptor fd = afd.getFileDescriptor();
long offset = afd.getStartOffset();
......@@ -385,6 +443,7 @@ public class MediaUtil {
}
return;
case REPL_ASSET:
mediaPlayer.setDataSource(replAssetPath(mediaPath));
return;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment