You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Paths.java 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /*
  2. * Copyright (C) 2016, Google Inc. and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.util;
  11. import static org.eclipse.jgit.lib.FileMode.TYPE_MASK;
  12. import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
  13. /**
  14. * Utility functions for paths inside of a Git repository.
  15. *
  16. * @since 4.2
  17. */
  18. public class Paths {
  19. /**
  20. * Remove trailing {@code '/'} if present.
  21. *
  22. * @param path
  23. * input path to potentially remove trailing {@code '/'} from.
  24. * @return null if {@code path == null}; {@code path} after removing a
  25. * trailing {@code '/'}.
  26. */
  27. public static String stripTrailingSeparator(String path) {
  28. if (path == null || path.isEmpty()) {
  29. return path;
  30. }
  31. int i = path.length();
  32. if (path.charAt(path.length() - 1) != '/') {
  33. return path;
  34. }
  35. do {
  36. i--;
  37. } while (path.charAt(i - 1) == '/');
  38. return path.substring(0, i);
  39. }
  40. /**
  41. * Compare two paths according to Git path sort ordering rules.
  42. *
  43. * @param aPath
  44. * first path buffer. The range {@code [aPos, aEnd)} is used.
  45. * @param aPos
  46. * index into {@code aPath} where the first path starts.
  47. * @param aEnd
  48. * 1 past last index of {@code aPath}.
  49. * @param aMode
  50. * mode of the first file. Trees are sorted as though
  51. * {@code aPath[aEnd] == '/'}, even if aEnd does not exist.
  52. * @param bPath
  53. * second path buffer. The range {@code [bPos, bEnd)} is used.
  54. * @param bPos
  55. * index into {@code bPath} where the second path starts.
  56. * @param bEnd
  57. * 1 past last index of {@code bPath}.
  58. * @param bMode
  59. * mode of the second file. Trees are sorted as though
  60. * {@code bPath[bEnd] == '/'}, even if bEnd does not exist.
  61. * @return <0 if {@code aPath} sorts before {@code bPath};
  62. * 0 if the paths are the same;
  63. * >0 if {@code aPath} sorts after {@code bPath}.
  64. */
  65. public static int compare(byte[] aPath, int aPos, int aEnd, int aMode,
  66. byte[] bPath, int bPos, int bEnd, int bMode) {
  67. int cmp = coreCompare(
  68. aPath, aPos, aEnd, aMode,
  69. bPath, bPos, bEnd, bMode);
  70. if (cmp == 0) {
  71. cmp = lastPathChar(aMode) - lastPathChar(bMode);
  72. }
  73. return cmp;
  74. }
  75. /**
  76. * Compare two paths, checking for identical name.
  77. * <p>
  78. * Unlike {@code compare} this method returns {@code 0} when the paths have
  79. * the same characters in their names, even if the mode differs. It is
  80. * intended for use in validation routines detecting duplicate entries.
  81. * <p>
  82. * Returns {@code 0} if the names are identical and a conflict exists
  83. * between {@code aPath} and {@code bPath}, as they share the same name.
  84. * <p>
  85. * Returns {@code <0} if all possibles occurrences of {@code aPath} sort
  86. * before {@code bPath} and no conflict can happen. In a properly sorted
  87. * tree there are no other occurrences of {@code aPath} and therefore there
  88. * are no duplicate names.
  89. * <p>
  90. * Returns {@code >0} when it is possible for a duplicate occurrence of
  91. * {@code aPath} to appear later, after {@code bPath}. Callers should
  92. * continue to examine candidates for {@code bPath} until the method returns
  93. * one of the other return values.
  94. *
  95. * @param aPath
  96. * first path buffer. The range {@code [aPos, aEnd)} is used.
  97. * @param aPos
  98. * index into {@code aPath} where the first path starts.
  99. * @param aEnd
  100. * 1 past last index of {@code aPath}.
  101. * @param bPath
  102. * second path buffer. The range {@code [bPos, bEnd)} is used.
  103. * @param bPos
  104. * index into {@code bPath} where the second path starts.
  105. * @param bEnd
  106. * 1 past last index of {@code bPath}.
  107. * @param bMode
  108. * mode of the second file. Trees are sorted as though
  109. * {@code bPath[bEnd] == '/'}, even if bEnd does not exist.
  110. * @return &lt;0 if no duplicate name could exist;
  111. * 0 if the paths have the same name;
  112. * &gt;0 other {@code bPath} should still be checked by caller.
  113. */
  114. public static int compareSameName(
  115. byte[] aPath, int aPos, int aEnd,
  116. byte[] bPath, int bPos, int bEnd, int bMode) {
  117. return coreCompare(
  118. aPath, aPos, aEnd, TYPE_TREE,
  119. bPath, bPos, bEnd, bMode);
  120. }
  121. private static int coreCompare(
  122. byte[] aPath, int aPos, int aEnd, int aMode,
  123. byte[] bPath, int bPos, int bEnd, int bMode) {
  124. while (aPos < aEnd && bPos < bEnd) {
  125. int cmp = (aPath[aPos++] & 0xff) - (bPath[bPos++] & 0xff);
  126. if (cmp != 0) {
  127. return cmp;
  128. }
  129. }
  130. if (aPos < aEnd) {
  131. return (aPath[aPos] & 0xff) - lastPathChar(bMode);
  132. }
  133. if (bPos < bEnd) {
  134. return lastPathChar(aMode) - (bPath[bPos] & 0xff);
  135. }
  136. return 0;
  137. }
  138. private static int lastPathChar(int mode) {
  139. if ((mode & TYPE_MASK) == TYPE_TREE) {
  140. return '/';
  141. }
  142. return 0;
  143. }
  144. private Paths() {
  145. }
  146. }