/* ****************************************************************************** * Copyright (c) 2006-2012 XMind Ltd. and others. * * This file is a part of XMind 3. XMind releases 3 and * above are dual-licensed under the Eclipse Public License (EPL), * which is available at http://www.eclipse.org/legal/epl-v10.html * and the GNU Lesser General Public License (LGPL), * which is available at http://www.gnu.org/licenses/lgpl.html * See http://www.xmind.net/license.html for details. * * Contributors: * XMind Ltd. - initial API and implementation *******************************************************************************/ package org.xmind.ui.internal.protocols; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * * @author Karelun Huang */ public class FilePathParser { private static final String SEP = System.getProperty("file.separator"); //$NON-NLS-1$ public static String toPath(String uri) { if (uri == null) return null; try { uri = decode(uri, true); } catch (Exception e) { } String path; if (uri.startsWith("file:")) //$NON-NLS-1$ path = uri.substring(5); else path = uri; if (path.startsWith("//")) //$NON-NLS-1$ path = path.substring(2); if (path.startsWith("/") //$NON-NLS-1$ && "win32".equals(System.getProperty("osgi.os"))) { //$NON-NLS-1$ //$NON-NLS-2$ path = path.substring(1); } return path; } public static String toURI(String path, boolean relative) { if (path == null) return null; if (File.separatorChar != '/') path = path.replace(File.separatorChar, '/'); return encode(relative ? "file:" + path : "file://" + path, true); //$NON-NLS-1$ //$NON-NLS-2$ } public static boolean isPathRelative(String path) { return !new File(path).isAbsolute(); } private static List<File> getRoutine(File file, List<File> routine) { File parent = file.getParentFile(); if (parent != null) { routine.add(0, parent); return getRoutine(parent, routine); } return routine; } private static int findStart(List<File> r1, List<File> r2) { int start; for (start = 0; start < r1.size() && start < r2.size(); start++) { if (!r1.get(start).equals(r2.get(start))) break; } return start; } public static String toRelativePath(String base, String absolutePath) { File file = new File(absolutePath); File baseFile = new File(base); List<File> routine = getRoutine(file, new ArrayList<File>()); List<File> baseRoutine = new ArrayList<File>(); baseRoutine.add(baseFile); baseRoutine = getRoutine(baseFile, baseRoutine); int start = findStart(routine, baseRoutine); StringBuilder sb = new StringBuilder(20); String sep = SEP; for (int i = start; i < baseRoutine.size(); i++) { sb.append(".."); //$NON-NLS-1$ sb.append(sep); } for (int i = start; i < routine.size(); i++) { sb.append(routine.get(i).getName()); sb.append(sep); } sb.append(file.getName()); return sb.toString(); } public static String toAbsolutePath(String base, String relativePath) { try { return new File(base, relativePath).getCanonicalPath(); } catch (IOException e) { return new File(base, relativePath).getAbsolutePath(); } } public static void main(String[] args) { String base = "/Applications/Utilities/Console.app/Contents/info.plist"; //$NON-NLS-1$ String absolutePath = "/Users/frankshaka/Desktop/a.xmind"; //$NON-NLS-1$ String relativePath = toRelativePath(base, absolutePath); System.out.println(relativePath); System.out.println(isPathRelative(relativePath)); System.out.println(toAbsolutePath(base, relativePath)); } /* * ECMA 3, 15.1.3 URI Handling Function Properties * * The following are implementations of the algorithms given in the ECMA * specification for the hidden functions 'Encode' and 'Decode'. */ private static String encode(String str, boolean fullUri) { byte[] utf8buf = null; StringBuffer sb = null; for (int k = 0, length = str.length(); k != length; ++k) { char C = str.charAt(k); if (encodeUnescaped(C, fullUri)) { if (sb != null) { sb.append(C); } } else { if (sb == null) { sb = new StringBuffer(length + 3); sb.append(str); sb.setLength(k); utf8buf = new byte[6]; } if (0xDC00 <= C && C <= 0xDFFF) { throw new IllegalArgumentException(); } int V; if (C < 0xD800 || 0xDBFF < C) { V = C; } else { k++; if (k == length) { throw new IllegalArgumentException(); } char C2 = str.charAt(k); if (!(0xDC00 <= C2 && C2 <= 0xDFFF)) { throw new IllegalArgumentException(); } V = ((C - 0xD800) << 10) + (C2 - 0xDC00) + 0x10000; } int L = oneUcs4ToUtf8Char(utf8buf, V); for (int j = 0; j < L; j++) { int d = 0xff & utf8buf[j]; sb.append('%'); sb.append(toHexChar(d >>> 4)); sb.append(toHexChar(d & 0xf)); } } } return (sb == null) ? str : sb.toString(); } private static String decode(String str, boolean fullUri) { char[] buf = null; int bufTop = 0; for (int k = 0, length = str.length(); k != length;) { char C = str.charAt(k); if (C != '%') { if (buf != null) { buf[bufTop++] = C; } ++k; } else { if (buf == null) { // decode always compress so result can not be bigger then // str.length() buf = new char[length]; str.getChars(0, k, buf, 0); bufTop = k; } int start = k; if (k + 3 > length) throw new IllegalArgumentException(); int B = unHex(str.charAt(k + 1), str.charAt(k + 2)); if (B < 0) throw new IllegalArgumentException(); k += 3; if ((B & 0x80) == 0) { C = (char) B; } else { // Decode UTF-8 sequence into ucs4Char and encode it into // UTF-16 int utf8Tail, ucs4Char, minUcs4Char; if ((B & 0xC0) == 0x80) { // First UTF-8 should be ouside 0x80..0xBF throw new IllegalArgumentException(); } else if ((B & 0x20) == 0) { utf8Tail = 1; ucs4Char = B & 0x1F; minUcs4Char = 0x80; } else if ((B & 0x10) == 0) { utf8Tail = 2; ucs4Char = B & 0x0F; minUcs4Char = 0x800; } else if ((B & 0x08) == 0) { utf8Tail = 3; ucs4Char = B & 0x07; minUcs4Char = 0x10000; } else if ((B & 0x04) == 0) { utf8Tail = 4; ucs4Char = B & 0x03; minUcs4Char = 0x200000; } else if ((B & 0x02) == 0) { utf8Tail = 5; ucs4Char = B & 0x01; minUcs4Char = 0x4000000; } else { // First UTF-8 can not be 0xFF or 0xFE throw new IllegalArgumentException(); } if (k + 3 * utf8Tail > length) throw new IllegalArgumentException(); for (int j = 0; j != utf8Tail; j++) { if (str.charAt(k) != '%') throw new IllegalArgumentException(); B = unHex(str.charAt(k + 1), str.charAt(k + 2)); if (B < 0 || (B & 0xC0) != 0x80) throw new IllegalArgumentException(); ucs4Char = (ucs4Char << 6) | (B & 0x3F); k += 3; } // Check for overlongs and other should-not-present codes if (ucs4Char < minUcs4Char || ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) { ucs4Char = 0xFFFD; } if (ucs4Char >= 0x10000) { ucs4Char -= 0x10000; if (ucs4Char > 0xFFFFF) throw new IllegalArgumentException(); char H = (char) ((ucs4Char >>> 10) + 0xD800); C = (char) ((ucs4Char & 0x3FF) + 0xDC00); buf[bufTop++] = H; } else { C = (char) ucs4Char; } } if (fullUri && URI_DECODE_RESERVED.indexOf(C) >= 0) { for (int x = start; x != k; x++) { buf[bufTop++] = str.charAt(x); } } else { buf[bufTop++] = C; } } } return (buf == null) ? str : new String(buf, 0, bufTop); } private static boolean encodeUnescaped(char c, boolean fullUri) { if (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9')) { return true; } if ("-_.!~*'()".indexOf(c) >= 0) //$NON-NLS-1$ return true; if (fullUri) { return URI_DECODE_RESERVED.indexOf(c) >= 0; } return false; } private static final String URI_DECODE_RESERVED = ";/?:@&=+$,#"; //$NON-NLS-1$ /* * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at * least 6 bytes long. Return the number of UTF-8 bytes of data written. */ private static int oneUcs4ToUtf8Char(byte[] utf8Buffer, int ucs4Char) { int utf8Length = 1; //JS_ASSERT(ucs4Char <= 0x7FFFFFFF); if ((ucs4Char & ~0x7F) == 0) utf8Buffer[0] = (byte) ucs4Char; else { int i; int a = ucs4Char >>> 11; utf8Length = 2; while (a != 0) { a >>>= 5; utf8Length++; } i = utf8Length; while (--i > 0) { utf8Buffer[i] = (byte) ((ucs4Char & 0x3F) | 0x80); ucs4Char >>>= 6; } utf8Buffer[0] = (byte) (0x100 - (1 << (8 - utf8Length)) + ucs4Char); } return utf8Length; } private static char toHexChar(int i) { if (i >> 4 != 0) throw new IllegalArgumentException(); return (char) ((i < 10) ? i + '0' : i - 10 + 'A'); } private static int unHex(char c) { if ('A' <= c && c <= 'F') { return c - 'A' + 10; } else if ('a' <= c && c <= 'f') { return c - 'a' + 10; } else if ('0' <= c && c <= '9') { return c - '0'; } else { return -1; } } private static int unHex(char c1, char c2) { int i1 = unHex(c1); int i2 = unHex(c2); if (i1 >= 0 && i2 >= 0) { return (i1 << 4) | i2; } return -1; } }