/common/zanata-common-util/src/main/java/org/zanata/util/PathUtil.java

https://github.com/hferentschik/zanata · Java · 183 lines · 96 code · 26 blank · 61 comment · 19 complexity · 2a396c73943cdf35666aad43b2e0c4b8 MD5 · raw file

  1. package org.zanata.util;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.util.regex.Pattern;
  5. import org.apache.commons.io.FilenameUtils;
  6. /**
  7. * From
  8. * http://stackoverflow.com/questions/204784/how-to-construct-a-relative-path
  9. * -in-java-from-two-absolute-paths-or-urls/3054692#3054692
  10. *
  11. */
  12. public class PathUtil
  13. {
  14. private static final String FILESEP = System.getProperty("file.separator");
  15. /**
  16. * Get the relative path from one file to another, specifying the directory
  17. * separator. If one of the provided resources does not exist, it is assumed
  18. * to be a file unless it ends with '/' or '\'.
  19. *
  20. * @param target targetPath is calculated to this file
  21. * @param base basePath is calculated from this file
  22. * @return
  23. * @throws PathResolutionException if no relative path exists
  24. */
  25. public static String getRelativePath(String targetPath, String basePath) throws PathResolutionException
  26. {
  27. return getRelativePath(targetPath, basePath, FILESEP);
  28. }
  29. /**
  30. * Get the relative path from one file to another, specifying the directory
  31. * separator. If one of the provided resources does not exist, it is assumed
  32. * to be a file unless it ends with '/' or '\'.
  33. *
  34. * @param target targetPath is calculated to this file
  35. * @param base basePath is calculated from this file
  36. * @param separator directory separator. The platform default is not assumed
  37. * so that we can test Unix behaviour when running on Windows (for
  38. * example)
  39. * @return
  40. * @throws PathResolutionException if no relative path exists
  41. */
  42. public static String getRelativePath(String targetPath, String basePath, String pathSeparator) throws PathResolutionException
  43. {
  44. // Normalize the paths
  45. String normalizedTargetPath = FilenameUtils.normalizeNoEndSeparator(targetPath);
  46. String normalizedBasePath = FilenameUtils.normalizeNoEndSeparator(basePath);
  47. // Undo the changes to the separators made by normalization
  48. if (pathSeparator.equals("/"))
  49. {
  50. normalizedTargetPath = FilenameUtils.separatorsToUnix(normalizedTargetPath);
  51. normalizedBasePath = FilenameUtils.separatorsToUnix(normalizedBasePath);
  52. }
  53. else if (pathSeparator.equals("\\"))
  54. {
  55. normalizedTargetPath = FilenameUtils.separatorsToWindows(normalizedTargetPath);
  56. normalizedBasePath = FilenameUtils.separatorsToWindows(normalizedBasePath);
  57. }
  58. else
  59. {
  60. throw new IllegalArgumentException("Unrecognised dir separator '" + pathSeparator + "'");
  61. }
  62. String[] base = normalizedBasePath.split(Pattern.quote(pathSeparator));
  63. String[] target = normalizedTargetPath.split(Pattern.quote(pathSeparator));
  64. // First get all the common elements. Store them as a string,
  65. // and also count how many of them there are.
  66. StringBuffer common = new StringBuffer();
  67. int commonIndex = 0;
  68. while (commonIndex < target.length && commonIndex < base.length && target[commonIndex].equals(base[commonIndex]))
  69. {
  70. common.append(target[commonIndex] + pathSeparator);
  71. commonIndex++;
  72. }
  73. if (commonIndex == 0)
  74. {
  75. // No single common path element. This most
  76. // likely indicates differing drive letters, like C: and D:.
  77. // These paths cannot be relativized.
  78. throw new PathResolutionException("No common path element found for '" + normalizedTargetPath + "' and '" + normalizedBasePath + "'");
  79. }
  80. // The number of directories we have to backtrack depends on whether the
  81. // base is a file or a dir
  82. // For example, the relative path from
  83. //
  84. // /foo/bar/baz/gg/ff to /foo/bar/baz
  85. //
  86. // ".." if ff is a file
  87. // "../.." if ff is a directory
  88. //
  89. // The following is a heuristic to figure out if the base refers to a file
  90. // or dir. It's not perfect, because
  91. // the resource referred to by this path may not actually exist, but it's
  92. // the best I can do
  93. boolean baseIsFile = true;
  94. File baseResource = new File(normalizedBasePath);
  95. if (baseResource.exists())
  96. {
  97. baseIsFile = baseResource.isFile();
  98. }
  99. else if (basePath.endsWith(pathSeparator))
  100. {
  101. baseIsFile = false;
  102. }
  103. StringBuffer relative = new StringBuffer();
  104. if (base.length != commonIndex)
  105. {
  106. int numDirsUp = baseIsFile ? base.length - commonIndex - 1 : base.length - commonIndex;
  107. for (int i = 0; i < numDirsUp; i++)
  108. {
  109. relative.append(".." + pathSeparator);
  110. }
  111. }
  112. relative.append(normalizedTargetPath.substring(common.length()));
  113. return relative.toString();
  114. }
  115. /**
  116. * Returns relative path from parentDir to f. NB: assumes that f is inside
  117. * parentDir.
  118. * @param f a file inside parentDir
  119. * @param parentDir
  120. *
  121. * @return
  122. * @throws IOException
  123. * @throws PathResolutionException
  124. */
  125. public static String getSubPath(File f, File parentDir) throws IOException, PathResolutionException
  126. {
  127. // we could use canonical path here, but we don't want symlinks to be
  128. // resolved
  129. String dirPath = parentDir.getAbsolutePath();
  130. String filePath = f.getAbsolutePath();
  131. if (!filePath.startsWith(dirPath))
  132. {
  133. throw new PathResolutionException("can't find relative path from " + parentDir + " to " + f);
  134. }
  135. int skipSize = dirPath.length();
  136. if (!dirPath.endsWith(FILESEP))
  137. skipSize++;
  138. return filePath.substring(skipSize);
  139. }
  140. static class PathResolutionException extends RuntimeException
  141. {
  142. private static final long serialVersionUID = 1L;
  143. PathResolutionException(String msg)
  144. {
  145. super(msg);
  146. }
  147. }
  148. public static boolean makeParents(File f) throws IOException
  149. {
  150. File parent = f.getCanonicalFile().getParentFile();
  151. if (parent == null || parent.exists())
  152. {
  153. return false;
  154. }
  155. return parent.mkdirs();
  156. }
  157. }