/*
* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package com.nokia.mj.impl.fileutils;
/**
* FileURL manages URL/Path handling. It is responsible to manage paths which
* have path separators specific to the platform. It will also manage
* differences between URL and Absolute path and make it transparent to use
* from.
*/
final class FileURL
{
private static String FILE_PREFIX = "file://";
private static String FILE_VALID_START_1 = "file:///";
private static String FILE_VALID_START_2 = "file://localhost/";
public static final String S60_FILE_SEPARATOR = "\\";
public static final String LINUX_FILE_SEPARATOR = "/";
private boolean iIsS60Path;
// Contains the unescaped path.
private String iUnescapedPath;
// Contains the absolute path to the current target.
private String iTargetPath;
// Contains the name of the target.
private String iTargetName;
private String iSeparatorUsed;
/**
* Constructor for FileURL.
*
* @param aName
* Can contain either an Absolute path name or a File URL.
* <p>
* Path separator in Absolute path can be either "/" or platform
* specific path separator. This is specified in file.separator
* system property.
* <p>
* In case of file URL being passed as parameter, it must follow
* the format of a fully-qualified, absolute path file name as
* described by the file URL format in IETF RFCs 1738 & 2396.
*/
public FileURL(String aName)
{
iIsS60Path = false;
String initialPath = aName;
String path = "";
if (aName.trim().length() <= 0)
{
throw new IllegalArgumentException();
}
if (aName.startsWith(FILE_PREFIX))
{
validateUrlStart(aName);
// aName is a url of the form file://<host>/<path>
// We need just the absolute path. So we extract it.
path = initialPath.substring(initialPath.indexOf('/', FILE_PREFIX
.length()) + 1);
// A file URL should have only "/" as file separator.
if (initialPath.indexOf(S60_FILE_SEPARATOR) != -1)
{
throw new IllegalArgumentException();
}
}
else
{
path = aName;
}
int endIndex = 0;
String tempName = path;
while (tempName.indexOf("//")!=-1)
{
endIndex = path.indexOf("//");
tempName = path.substring(0, endIndex);
if (endIndex < path.length())
{
tempName += path.substring(endIndex+1);
}
};
path = tempName;
// Check the sanity of path specified
checkPathValidity(path);
iUnescapedPath = path;
iIsS60Path = (iUnescapedPath.indexOf("\\") != -1);
int index = 0;
if (iIsS60Path)
{
iSeparatorUsed = "\\";
index = iUnescapedPath.lastIndexOf('\\',
iUnescapedPath.length() - 2);
}
else
{
iSeparatorUsed = "/";
index = iUnescapedPath
.lastIndexOf('/', iUnescapedPath.length() - 2);
}
iTargetName = iUnescapedPath.substring(index + 1);
iTargetPath = iUnescapedPath.substring(0, index + 1);
}
/**
* checks to see if URL begins with valid urls starts.<br/> It can either
* be file:/// or file://localhost/.
*
* @param url
* contains url passed to the constructor
*/
private void validateUrlStart(String url)
{
if (url.startsWith(FILE_VALID_START_1)
|| url.startsWith(FILE_VALID_START_2))
{
return;
}
throw new IllegalArgumentException();
}
/**
* Getter method to retrieve path to the parent directory of the current
* target.
*
* @return string containing absloute path to the target's parent.
*/
public String getPath()
{
return iTargetPath;
}
/**
* Getter method to retrieve name of the target.
*
* @return string containing the name of the target currently pointed to.
* This is derived from the url.
*/
public String getName()
{
return iTargetName;
}
/**
* Getter method to retrieve the full path of the target. It will contain
* path to the target and the target too as a complete url.
*
* @return the full path of the target
*/
public String getFullPath()
{
return iTargetPath + iTargetName;
}
protected String getSeparatorUsed()
{
return iSeparatorUsed;
}
/**
* Getter method to retrieve the file URL that can be passed as a parameter
* to FileConnection url. It will conform to File URL format in IETF RFCs
* 1738 & 2396.
*
* @return fully qualified File URL
*/
public String getUrl()
{
// Get URL must return escaped url.
String aUrl = FILE_VALID_START_1 + iTargetPath + iTargetName;
String value = "";
for (int i = 0; i < aUrl.length(); i++)
{
char ch = aUrl.charAt(i);
// Also replace "\\" with "/"
if (ch == '\\')
{
value += "/";
continue;
}
boolean isLetter = (ch >= 'a' && ch <= 'z')
|| (ch >= 'A' && ch <= 'Z');
boolean isDigit = (ch >= '0' && ch <= '9');
if (isLetter || isDigit)
{
value += ch;
}
else
{
value += stringToHex("" + ch);
}
}
return value;
}
/**
* Checks to see if the path passed is valid. Simple sanity checks for the
* path to point to a valid target.
*
* @param name
* contains an absolute path to a target.
*/
private static final void checkPathValidity(String name)
{
// Only in S60 file system, we have the concept of drives.
// So, ":" should only appear in File systems that have
// file.separator as '\\'
String separator = System.getProperty("file.separator");
// If platform is S60 like.
if (separator.equals(S60_FILE_SEPARATOR))
{
makeS60SpecificChecks(name);
}
else
{
makeLinuxSpecificChecks(name);
}
if ((name.indexOf('*') != -1) || (name.indexOf('?') != -1)
|| (name.indexOf('<') != -1) || (name.indexOf('>') != -1)
|| (name.indexOf("//") != -1) || name.endsWith("/."))
{
throw new IllegalArgumentException(name);
}
}
/**
* Makes checks that are specific to S60 like File System. This should be
* called only in case file.separator is "\\"
*
* @param name
* contians the path that needs to be checked for sanity.
*/
private static final void makeS60SpecificChecks(String name)
{
// First index of colon
int firstIndexOfColon = name.indexOf(':');
int indexOfColon = name.indexOf(':', firstIndexOfColon + 1);
if (indexOfColon != -1)
{
throw new IllegalArgumentException(name);
}
// In S60 like file system, it is possible to have "/" as the file
// separator as file url accepts it. But then, once there is "\\" as
// file separator, the entire url should follow the same convention.
if (name.indexOf(S60_FILE_SEPARATOR) != -1)
{
if (name.indexOf(LINUX_FILE_SEPARATOR) != -1)
{
// We must be having only one kind of separator
throw new IllegalArgumentException(name);
}
if (firstIndexOfColon > name.indexOf(S60_FILE_SEPARATOR))
{
throw new IllegalArgumentException(name);
}
}
else
{
if ((firstIndexOfColon > name.indexOf(LINUX_FILE_SEPARATOR)))
{
throw new IllegalArgumentException(name);
}
}
}
/**
* Makes checks that are specific to Linux like File System. This should be
* called only in case file.separator is "/"
*
* @param name
* contians the path that needs to be checked for sanity.
*/
private static final void makeLinuxSpecificChecks(String name)
{
if (name.indexOf(':') != -1)
{
throw new IllegalArgumentException(name);
}
if (name.indexOf(S60_FILE_SEPARATOR) != -1)
{
throw new IllegalArgumentException(name);
}
}
/**
* encodes a unicode string to a string of 4-digit hex numbers <br />
* Note: this method is slower than stringToHex(String,StringBuffer)
*
* @param java
* normal java string
* @return string of 4-digit hex numbers
*/
public static final String stringToHex(String str)
{
if (str == null)
return "";
int length = str.length();
StringBuffer result = new StringBuffer(length * 4);
stringToHex(str, result);
return result.toString();
}
/**
* encodes a unicode string to a string of 4-digit hex numbers.
*
* @param str
* normal java string
* @param out
* string of 4-digit hex numbers
*/
public static final void stringToHex(String str, StringBuffer out)
{
// To improve performance, implement a table-lookup instead of if/then cases.
if (str == null)
return;
int length = str.length();
for (int pos = 0; pos < length; pos++)
{
char cur_char = str.charAt(pos);
int this_char = (int) cur_char;
if (this_char < 127 && cur_char != ' ')
{
out.append((char) this_char);
continue;
}
for (int digit = 0; digit < 4; digit++)
{
int this_digit = this_char & 0xf000;
this_char = this_char << 4;
this_digit = (this_digit >> 12);
// We get 4 digits, so on 2nd we insert a % sign and then value
if (digit == 2)
out.append('%');
if (digit < 2)
continue;
if (this_digit >= 10)
out.append((char)(this_digit + 87));
else
out.append((char)(this_digit + 48));
}
}
}
}