1 /* 2 * Copyright (C) 2016, Google Inc. 3 * and other copyright owners as documented in the project's IP log. 4 * 5 * This program and the accompanying materials are made available 6 * under the terms of the Eclipse Distribution License v1.0 which 7 * accompanies this distribution, is reproduced below, and is 8 * available at http://www.eclipse.org/org/documents/edl-v10.php 9 * 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or 13 * without modification, are permitted provided that the following 14 * conditions are met: 15 * 16 * - Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 19 * - Redistributions in binary form must reproduce the above 20 * copyright notice, this list of conditions and the following 21 * disclaimer in the documentation and/or other materials provided 22 * with the distribution. 23 * 24 * - Neither the name of the Eclipse Foundation, Inc. nor the 25 * names of its contributors may be used to endorse or promote 26 * products derived from this software without specific prior 27 * written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 30 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 32 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 34 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 35 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 36 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 38 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 41 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 42 */ 43 44 package org.eclipse.jgit.util; 45 46 import static org.eclipse.jgit.lib.FileMode.TYPE_MASK; 47 import static org.eclipse.jgit.lib.FileMode.TYPE_TREE; 48 49 /** 50 * Utility functions for paths inside of a Git repository. 51 * 52 * @since 4.2 53 */ 54 public class Paths { 55 /** 56 * Remove trailing {@code '/'} if present. 57 * 58 * @param path 59 * input path to potentially remove trailing {@code '/'} from. 60 * @return null if {@code path == null}; {@code path} after removing a 61 * trailing {@code '/'}. 62 */ 63 public static String stripTrailingSeparator(String path) { 64 if (path == null || path.isEmpty()) { 65 return path; 66 } 67 68 int i = path.length(); 69 if (path.charAt(path.length() - 1) != '/') { 70 return path; 71 } 72 do { 73 i--; 74 } while (path.charAt(i - 1) == '/'); 75 return path.substring(0, i); 76 } 77 78 /** 79 * Compare two paths according to Git path sort ordering rules. 80 * 81 * @param aPath 82 * first path buffer. The range {@code [aPos, aEnd)} is used. 83 * @param aPos 84 * index into {@code aPath} where the first path starts. 85 * @param aEnd 86 * 1 past last index of {@code aPath}. 87 * @param aMode 88 * mode of the first file. Trees are sorted as though 89 * {@code aPath[aEnd] == '/'}, even if aEnd does not exist. 90 * @param bPath 91 * second path buffer. The range {@code [bPos, bEnd)} is used. 92 * @param bPos 93 * index into {@code bPath} where the second path starts. 94 * @param bEnd 95 * 1 past last index of {@code bPath}. 96 * @param bMode 97 * mode of the second file. Trees are sorted as though 98 * {@code bPath[bEnd] == '/'}, even if bEnd does not exist. 99 * @return <0 if {@code aPath} sorts before {@code bPath}; 100 * 0 if the paths are the same; 101 * >0 if {@code aPath} sorts after {@code bPath}. 102 */ 103 public static int compare(byte[] aPath, int aPos, int aEnd, int aMode, 104 byte[] bPath, int bPos, int bEnd, int bMode) { 105 int cmp = coreCompare( 106 aPath, aPos, aEnd, aMode, 107 bPath, bPos, bEnd, bMode); 108 if (cmp == 0) { 109 cmp = lastPathChar(aMode) - lastPathChar(bMode); 110 } 111 return cmp; 112 } 113 114 /** 115 * Compare two paths, checking for identical name. 116 * <p> 117 * Unlike {@code compare} this method returns {@code 0} when the paths have 118 * the same characters in their names, even if the mode differs. It is 119 * intended for use in validation routines detecting duplicate entries. 120 * <p> 121 * Returns {@code 0} if the names are identical and a conflict exists 122 * between {@code aPath} and {@code bPath}, as they share the same name. 123 * <p> 124 * Returns {@code <0} if all possibles occurrences of {@code aPath} sort 125 * before {@code bPath} and no conflict can happen. In a properly sorted 126 * tree there are no other occurrences of {@code aPath} and therefore there 127 * are no duplicate names. 128 * <p> 129 * Returns {@code >0} when it is possible for a duplicate occurrence of 130 * {@code aPath} to appear later, after {@code bPath}. Callers should 131 * continue to examine candidates for {@code bPath} until the method returns 132 * one of the other return values. 133 * 134 * @param aPath 135 * first path buffer. The range {@code [aPos, aEnd)} is used. 136 * @param aPos 137 * index into {@code aPath} where the first path starts. 138 * @param aEnd 139 * 1 past last index of {@code aPath}. 140 * @param bPath 141 * second path buffer. The range {@code [bPos, bEnd)} is used. 142 * @param bPos 143 * index into {@code bPath} where the second path starts. 144 * @param bEnd 145 * 1 past last index of {@code bPath}. 146 * @param bMode 147 * mode of the second file. Trees are sorted as though 148 * {@code bPath[bEnd] == '/'}, even if bEnd does not exist. 149 * @return <0 if no duplicate name could exist; 150 * 0 if the paths have the same name; 151 * >0 other {@code bPath} should still be checked by caller. 152 */ 153 public static int compareSameName( 154 byte[] aPath, int aPos, int aEnd, 155 byte[] bPath, int bPos, int bEnd, int bMode) { 156 return coreCompare( 157 aPath, aPos, aEnd, TYPE_TREE, 158 bPath, bPos, bEnd, bMode); 159 } 160 161 private static int coreCompare( 162 byte[] aPath, int aPos, int aEnd, int aMode, 163 byte[] bPath, int bPos, int bEnd, int bMode) { 164 while (aPos < aEnd && bPos < bEnd) { 165 int cmp = (aPath[aPos++] & 0xff) - (bPath[bPos++] & 0xff); 166 if (cmp != 0) { 167 return cmp; 168 } 169 } 170 if (aPos < aEnd) { 171 return (aPath[aPos] & 0xff) - lastPathChar(bMode); 172 } 173 if (bPos < bEnd) { 174 return lastPathChar(aMode) - (bPath[bPos] & 0xff); 175 } 176 return 0; 177 } 178 179 private static int lastPathChar(int mode) { 180 if ((mode & TYPE_MASK) == TYPE_TREE) { 181 return '/'; 182 } 183 return 0; 184 } 185 186 private Paths() { 187 } 188 }