GitObject.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. package info.knacki.pass.git.entities;
  2. import android.support.annotation.NonNull;
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.File;
  5. import java.io.FileInputStream;
  6. import java.io.FileNotFoundException;
  7. import java.io.IOException;
  8. import java.util.ArrayDeque;
  9. import java.util.HashMap;
  10. import java.util.Map;
  11. import java.util.SortedSet;
  12. import java.util.TreeSet;
  13. import java.util.logging.Logger;
  14. import info.knacki.pass.git.GitSha1;
  15. import info.knacki.pass.io.CharsetHelper;
  16. import info.knacki.pass.io.FileUtils;
  17. public abstract class GitObject implements Comparable<GitObject>, GitPackable {
  18. GitTree fParent;
  19. final byte[] fMode;
  20. final String fName;
  21. byte[] fSha1;
  22. @Override
  23. public int compareTo(@NonNull GitObject gitObject) {
  24. return fName.toLowerCase().compareTo(gitObject.fName.toLowerCase());
  25. }
  26. public static class GitBlob extends GitObject {
  27. private File fFile;
  28. private GitBlob(GitTree parent, byte[] mode, String name, byte[] sha1) {
  29. super(parent, mode, name, sha1);
  30. fFile = null;
  31. }
  32. private GitBlob(GitTree parent, GitBlob copy) {
  33. this(parent, copy.fMode, copy.fName, copy.fSha1);
  34. }
  35. GitBlob(GitTree parent, String filename, File f) {
  36. this(parent, new byte[] { 49, 48, 48, 54, 52, 52 }, filename, GitSha1.getRawSha1OfFile(f));
  37. fFile = f;
  38. }
  39. @Override
  40. public eType GetPackableType() {
  41. return eType.eType_Blob;
  42. }
  43. public static byte[] GetFilePack(File f) throws IOException {
  44. return FileUtils.ReadAllStream(new FileInputStream(f));
  45. }
  46. @Override
  47. public byte[] GetPack() {
  48. try
  49. {
  50. return GetFilePack(fFile);
  51. }
  52. catch (FileNotFoundException e) {
  53. return null;
  54. }
  55. catch(IOException t) {
  56. t.printStackTrace();
  57. }
  58. return null;
  59. }
  60. }
  61. public static class GitRawData extends GitBlob {
  62. private final byte[] fData;
  63. GitRawData(byte[] sha1, byte[] data) {
  64. super(null, new byte[] { 49, 48, 48, 54, 52, 52 }, "", sha1);
  65. fData = data;
  66. }
  67. public GitRawData(GitTree parent, byte[] mode, String name, byte[] sha1) {
  68. super(parent, mode, name, sha1);
  69. fData = null;
  70. }
  71. public GitRawData(GitTree parent, GitBlob emptyBlob, GitRawData prev) {
  72. super(parent, new byte[] { 49, 48, 48, 54, 52, 52 }, emptyBlob.GetFilename(), prev.GetHash());
  73. fData = prev.fData;
  74. }
  75. @Override
  76. public eType GetPackableType() {
  77. return eType.eType_Blob;
  78. }
  79. @Override
  80. public byte[] GetPack() {
  81. return GetData();
  82. }
  83. public byte[] GetData() { return fData; }
  84. }
  85. public static class GitTree extends GitObject {
  86. HashMap<String, GitObject> fItems = null;
  87. ArrayDeque<String> fItemOrder;
  88. private GitTree(GitTree parent, byte[] mode, String name, byte[] sha1) {
  89. super(parent, mode, name, sha1);
  90. }
  91. GitTree(GitTree parent, GitTree copy) {
  92. this(parent, copy.fMode, copy.fName, copy.fSha1);
  93. Initialize();
  94. for (Map.Entry<String, GitObject> o: copy.fItems.entrySet()) {
  95. GitObject src = o.getValue();
  96. GitObject oCopy = src instanceof GitTree ? new GitTree(this, (GitTree) src) : new GitBlob(this, (GitBlob) src);
  97. fItems.put(o.getKey(), oCopy);
  98. }
  99. for (String i: copy.fItemOrder)
  100. fItemOrder.push(i);
  101. }
  102. GitTree(byte[] sha1) {
  103. super(null, new byte[] { '4', '0', '0', '0', '0' }, "", sha1);
  104. }
  105. private GitTree(GitTree parent, String filename) {
  106. super(parent, new byte[] { '4', '0', '0', '0', '0' }, filename, null);
  107. }
  108. public GitTree(GitTree parent, GitTree content, String filename) {
  109. this(parent, filename);
  110. fItems = content.fItems;
  111. fItemOrder = content.fItemOrder;
  112. for (GitObject i: fItems.values()) {
  113. i.SetParent(this);
  114. }
  115. }
  116. public GitTree Initialize() {
  117. fItems = new HashMap<>();
  118. fItemOrder = new ArrayDeque<>();
  119. return this;
  120. }
  121. boolean IsInitialized() {
  122. return null != fItems;
  123. }
  124. public GitObject GetObject(String name) {
  125. return fItems.get(name);
  126. }
  127. public GitObject GetObjectFullPath(String path) {
  128. if ('/' == path.charAt(0))
  129. path = path.substring(1);
  130. final int nextSlash = path.indexOf('/');
  131. final String filename = nextSlash == -1 ? path : (path.substring(0, nextSlash));
  132. final GitObject obj = GetObject(filename);
  133. if (obj == null || (obj instanceof GitBlob && nextSlash != -1))
  134. return null;
  135. if (nextSlash == -1)
  136. return obj;
  137. return ((GitTree) obj).GetObjectFullPath(path.substring(nextSlash +1));
  138. }
  139. public GitTree AddItem(GitObject item) {
  140. fItems.put(item.fName, item);
  141. fItemOrder.push(item.fName);
  142. return this;
  143. }
  144. public GitTree FindNextNotInitializedTree() {
  145. if (!IsInitialized())
  146. return this;
  147. for (GitObject obj: fItems.values()) {
  148. if (!(obj instanceof GitTree))
  149. continue;
  150. GitTree child = ((GitTree) obj).FindNextNotInitializedTree();
  151. if (child != null)
  152. return child;
  153. }
  154. return null;
  155. }
  156. public Iterable<GitObject> GetObjects() {
  157. ArrayDeque<GitObject> objects = new ArrayDeque<>();
  158. for (String i: fItemOrder)
  159. objects.push(fItems.get(i));
  160. return objects;
  161. }
  162. public Iterable<GitBlob> GetBlobs() {
  163. SortedSet<GitBlob> objects = new TreeSet<>();
  164. for (GitObject i: fItems.values())
  165. if (i instanceof GitBlob)
  166. objects.add((GitBlob) i);
  167. return objects;
  168. }
  169. public Iterable<GitTree> GetTrees() {
  170. SortedSet<GitTree> objects = new TreeSet<>();
  171. for (GitObject i: fItems.values())
  172. if (i instanceof GitTree)
  173. objects.add((GitTree) i);
  174. return objects;
  175. }
  176. public String GetPath() {
  177. if (fParent != null)
  178. return fParent.GetPath() +GetFilename() +"/";
  179. return GetFilename() +"/";
  180. }
  181. public void SetRealObject(String key, GitObject obj) {
  182. if (fItems.containsKey(key)) {
  183. fItems.put(key, obj);
  184. }
  185. }
  186. private void FindAllBlobs(HashMap<String, GitBlob> data) {
  187. String rootPath = GetPath();
  188. for (GitBlob i: GetBlobs())
  189. data.put(rootPath +i.GetFilename(), i);
  190. for (GitTree i: GetTrees())
  191. i.FindAllBlobs(data);
  192. }
  193. public HashMap<String, GitBlob> FindAllBlobs() {
  194. HashMap<String, GitBlob> result = new HashMap<>();
  195. FindAllBlobs(result);
  196. return result;
  197. }
  198. public boolean IsRoot() {
  199. return fParent == null;
  200. }
  201. public GitObject AddItem(String path, File f) {
  202. if ('/' == path.charAt(0))
  203. path = path.substring(1);
  204. final int nextSlash = path.indexOf('/');
  205. final String filename = nextSlash == -1 ? path : (path.substring(0, nextSlash));
  206. final GitObject obj = GetObject(filename);
  207. final GitObject newItem;
  208. if (obj == null) {
  209. if (nextSlash != -1) {
  210. GitTree child = new GitTree(this, filename).Initialize();
  211. newItem = child.AddItem(path.substring(nextSlash + 1), f);
  212. fItems.put(filename, child);
  213. } else {
  214. newItem = new GitBlob(this, filename, f);
  215. fItems.put(filename, newItem);
  216. }
  217. fItemOrder.push(filename);
  218. } else if (nextSlash == -1) {
  219. Remove(filename);
  220. newItem = new GitBlob(this, filename, f);
  221. fItems.put(filename, newItem);
  222. fItemOrder.push(filename);
  223. } else {
  224. newItem = ((GitTree) obj).AddItem(path.substring(nextSlash + 1), f);
  225. }
  226. fSha1 = null;
  227. return newItem;
  228. }
  229. public GitTree Remove(String filename) {
  230. fItems.remove(filename);
  231. fItemOrder.remove(filename);
  232. fSha1 = null;
  233. return this;
  234. }
  235. public void Fill(byte[] data) {
  236. int i = 0;
  237. while (i < data.length) {
  238. byte[] mode = null;
  239. int len;
  240. for (len =0; i +len <data.length && len < 8; ++len) {
  241. if (data[i + len] == ' ') {
  242. mode = new byte[len];
  243. System.arraycopy(data, i, mode, 0, len);
  244. i += len + 1;
  245. break;
  246. }
  247. }
  248. if (mode == null)
  249. break;
  250. len =0;
  251. while (i +len < data.length && data[i +len] != 0)
  252. ++len;
  253. byte[] filenameBytes = new byte[len];
  254. System.arraycopy(data, i, filenameBytes, 0, len);
  255. i += len +1;
  256. if (i +20 > data.length)
  257. break;
  258. byte []sha1 = new byte[20];
  259. System.arraycopy(data, i, sha1, 0, 20);
  260. i += 20;
  261. AddItem(GitObject.factory(this, mode, CharsetHelper.ByteArrayToString(filenameBytes), sha1));
  262. }
  263. }
  264. @Override
  265. public eType GetPackableType() {
  266. return eType.eType_Tree;
  267. }
  268. @Override
  269. public byte[] GetPack() {
  270. try {
  271. ByteArrayOutputStream str = new ByteArrayOutputStream();
  272. for (GitObject go : GetObjects()) {
  273. str.write((go.GetMode() + ' ').getBytes());
  274. if (go.fName.equals("Aa") || "Oo".equals(go.fName))
  275. Logger.getLogger(GitObject.class.getName()).severe("out file in tree " +go.GetGitPath() +"$" + GitSha1.BytesToString(go.GetHash()));
  276. str.write(go.GetFilename().getBytes());
  277. str.write(new byte[]{0});
  278. str.write(go.GetHash());
  279. }
  280. return str.toByteArray();
  281. }
  282. catch (IOException e) {
  283. return null;
  284. }
  285. }
  286. @Override
  287. public byte[] GetHash() {
  288. if (null != fSha1)
  289. return fSha1;
  290. return fSha1 = GitSha1.getRawSha1OfPackable(this);
  291. }
  292. }
  293. private GitObject(GitTree parent, byte[] mode, String name, byte[] sha1) {
  294. fParent = parent;
  295. fMode = mode;
  296. fName = name;
  297. fSha1 = sha1;
  298. }
  299. public static GitObject factory(GitTree parent, byte[] mode, String name, byte[] sha1) {
  300. if (mode[0] == '4') {
  301. return new GitTree(parent, mode, name, sha1);
  302. }
  303. return new GitRawData(parent, mode, name, sha1);
  304. }
  305. public byte[] GetHash() {
  306. return fSha1;
  307. }
  308. public String GetFilename() {
  309. return fName;
  310. }
  311. public String GetGitPath() {
  312. StringBuilder sb = new StringBuilder();
  313. GitTree parent = fParent;
  314. sb.insert(0, fName);
  315. while (parent != null) {
  316. sb.insert(0, parent.fName + "/");
  317. parent = parent.fParent;
  318. }
  319. return sb.toString();
  320. }
  321. public GitTree GetParent() {
  322. return fParent;
  323. }
  324. private void SetParent(GitTree t) { fParent = t; }
  325. public String GetMode() {
  326. return new String(fMode);
  327. }
  328. public int GetDepth() {
  329. return fParent == null ? 1 : fParent.GetDepth() +1;
  330. }
  331. }