comparison src/org/tmatesoft/hg/internal/StoragePathHelper.java @ 74:6f1b88693d48

Complete refactoring to org.tmatesoft
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Mon, 24 Jan 2011 03:14:45 +0100
parents
children 4222b04f34ee
comparison
equal deleted inserted replaced
73:0d279bcc4442 74:6f1b88693d48
1 /*
2 * Copyright (c) 2011 TMate Software Ltd
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * For information on how to redistribute this software under
14 * the terms of a license other than GNU General Public License
15 * contact TMate Software at support@svnkit.com
16 */
17 package org.tmatesoft.hg.internal;
18
19 import java.util.Arrays;
20 import java.util.TreeSet;
21
22 import org.tmatesoft.hg.repo.HgRepository;
23 import org.tmatesoft.hg.util.PathRewrite;
24
25 /**
26 * @see http://mercurial.selenic.com/wiki/CaseFoldingPlan
27 *
28 * @author Artem Tikhomirov
29 * @author TMate Software Ltd.
30 */
31 class StoragePathHelper implements PathRewrite {
32
33 private final boolean store;
34 private final boolean fncache;
35 private final boolean dotencode;
36
37 public StoragePathHelper(boolean isStore, boolean isFncache, boolean isDotencode) {
38 store = isStore;
39 fncache = isFncache;
40 dotencode = isDotencode;
41 }
42
43 // FIXME document what path argument is, whether it includes .i or .d, and whether it's 'normalized' (slashes) or not.
44 // since .hg/store keeps both .i files and files without extension (e.g. fncache), guees, for data == false
45 // we shall assume path has extension
46 // FIXME much more to be done, see store.py:_hybridencode
47 public String rewrite(String path) {
48 final String STR_STORE = "store/";
49 final String STR_DATA = "data/";
50 final String STR_DH = "dh/";
51
52 path = path.replace(".hg/", ".hg.hg/").replace(".i/", ".i.hg/").replace(".d/", ".d.hg/");
53 StringBuilder sb = new StringBuilder(path.length() << 1);
54 if (store || fncache) {
55 // encodefilename
56 final String reservedChars = "\\:*?\"<>|";
57 // in fact, \\ is unlikely to match, ever - we've replaced all of them already, above. Just regards to store.py
58 int x;
59 char[] hexByte = new char[2];
60 for (int i = 0; i < path.length(); i++) {
61 final char ch = path.charAt(i);
62 if (ch >= 'a' && ch <= 'z') {
63 sb.append(ch); // POIRAE
64 } else if (ch >= 'A' && ch <= 'Z') {
65 sb.append('_');
66 sb.append(Character.toLowerCase(ch)); // Perhaps, (char) (((int) ch) + 32)? Even better, |= 0x20?
67 } else if ( (x = reservedChars.indexOf(ch)) != -1) {
68 sb.append('~');
69 sb.append(toHexByte(reservedChars.charAt(x), hexByte));
70 } else if ((ch >= '~' /*126*/ && ch <= 255) || ch < ' ' /*32*/) {
71 sb.append('~');
72 sb.append(toHexByte(ch, hexByte));
73 } else if (ch == '_') {
74 // note, encoding from store.py:_buildencodefun and :_build_lower_encodefun
75 // differ in the way they process '_' (latter doesn't escape it)
76 sb.append('_');
77 sb.append('_');
78 } else {
79 sb.append(ch);
80 }
81 }
82 // auxencode
83 if (fncache) {
84 x = 0; // last segment start
85 final TreeSet<String> windowsReservedFilenames = new TreeSet<String>();
86 windowsReservedFilenames.addAll(Arrays.asList("con prn aux nul com1 com2 com3 com4 com5 com6 com7 com8 com9 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9".split(" ")));
87 do {
88 int i = sb.indexOf("/", x);
89 if (i == -1) {
90 i = sb.length();
91 }
92 // windows reserved filenames are at least of length 3
93 if (i - x >= 3) {
94 boolean found = false;
95 if (i-x == 3) {
96 found = windowsReservedFilenames.contains(sb.subSequence(x, i));
97 } else if (sb.charAt(x+3) == '.') { // implicit i-x > 3
98 found = windowsReservedFilenames.contains(sb.subSequence(x, x+3));
99 } else if (i-x > 4 && sb.charAt(x+4) == '.') {
100 found = windowsReservedFilenames.contains(sb.subSequence(x, x+4));
101 }
102 if (found) {
103 sb.setCharAt(x, '~');
104 sb.insert(x+1, toHexByte(sb.charAt(x+2), hexByte));
105 i += 2;
106 }
107 }
108 if (dotencode && (sb.charAt(x) == '.' || sb.charAt(x) == ' ')) {
109 sb.insert(x+1, toHexByte(sb.charAt(x), hexByte));
110 sb.setCharAt(x, '~'); // setChar *after* charAt/insert to get ~2e, not ~7e for '.'
111 i += 2;
112 }
113 x = i+1;
114 } while (x < sb.length());
115 }
116 }
117 final int MAX_PATH_LEN_IN_HGSTORE = 120;
118 if (fncache && (sb.length() + STR_DATA.length() > MAX_PATH_LEN_IN_HGSTORE)) {
119 throw HgRepository.notImplemented(); // FIXME digest and fncache use
120 }
121 if (store) {
122 sb.insert(0, STR_STORE + STR_DATA);
123 }
124 sb.append(".i");
125 return sb.toString();
126 }
127
128 private static char[] toHexByte(int ch, char[] buf) {
129 assert buf.length > 1;
130 final String hexDigits = "0123456789abcdef";
131 buf[0] = hexDigits.charAt((ch & 0x00F0) >>> 4);
132 buf[1] = hexDigits.charAt(ch & 0x0F);
133 return buf;
134 }
135 }