Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/NewlineFilter.java @ 113:67ae317408c9
Filter implementation for newline translation
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Wed, 02 Feb 2011 21:19:02 +0100 |
parents | |
children | 46291ec605a0 |
comparison
equal
deleted
inserted
replaced
112:d488c7638b87 | 113:67ae317408c9 |
---|---|
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 static org.tmatesoft.hg.internal.Filter.Direction.FromRepo; | |
20 import static org.tmatesoft.hg.internal.Filter.Direction.ToRepo; | |
21 import static org.tmatesoft.hg.internal.KeywordFilter.copySlice; | |
22 | |
23 import java.io.File; | |
24 import java.io.FileInputStream; | |
25 import java.io.FileOutputStream; | |
26 import java.nio.ByteBuffer; | |
27 | |
28 import org.tmatesoft.hg.core.Path; | |
29 import org.tmatesoft.hg.repo.HgRepository; | |
30 | |
31 /** | |
32 * | |
33 * @author Artem Tikhomirov | |
34 * @author TMate Software Ltd. | |
35 */ | |
36 public class NewlineFilter implements Filter { | |
37 | |
38 // if allowInconsistent is true, filter simply pass incorrect newline characters (single \r or \r\n on *nix and single \n on Windows) as is, | |
39 // i.e. doesn't try to convert them into appropriate newline characters. XXX revisit if Keyword extension behaves differently | |
40 private final boolean allowInconsistent; | |
41 private final boolean winToNix; | |
42 | |
43 private NewlineFilter(boolean failIfInconsistent, int transform) { | |
44 winToNix = transform == 0; | |
45 allowInconsistent = !failIfInconsistent; | |
46 } | |
47 | |
48 public ByteBuffer filter(ByteBuffer src) { | |
49 if (winToNix) { | |
50 return win2nix(src); | |
51 } else { | |
52 return nix2win(src); | |
53 } | |
54 } | |
55 | |
56 private ByteBuffer win2nix(ByteBuffer src) { | |
57 int x = src.position(); // source index | |
58 int lookupStart = x; | |
59 ByteBuffer dst = null; | |
60 while (x < src.limit()) { | |
61 // x, lookupStart, ir and in are absolute positions within src buffer, which is never read with modifying operations | |
62 int ir = indexOf('\r', src, lookupStart); | |
63 int in = indexOf('\n', src, lookupStart); | |
64 if (ir == -1) { | |
65 if (in == -1 || allowInconsistent) { | |
66 if (dst != null) { | |
67 copySlice(src, x, src.limit(), dst); | |
68 x = src.limit(); // consumed all | |
69 } | |
70 break; | |
71 } else { | |
72 fail(src, in); | |
73 } | |
74 } | |
75 // in == -1 while ir != -1 may be valid case if ir is the last char of the buffer, we check below for that | |
76 if (in != -1 && in != ir+1 && !allowInconsistent) { | |
77 fail(src, in); | |
78 } | |
79 if (dst == null) { | |
80 dst = ByteBuffer.allocate(src.remaining()); | |
81 } | |
82 copySlice(src, x, ir, dst); | |
83 if (ir+1 == src.limit()) { | |
84 // last char of the buffer - | |
85 // consume src till that char and let next iteration work on it | |
86 x = ir; | |
87 break; | |
88 } | |
89 if (in != ir + 1) { | |
90 x = ir+1; // generally in, but if allowInconsistent==true and \r is not followed by \n, then | |
91 // cases like "one \r two \r\n three" shall be processed correctly (second pair would be ignored if x==in) | |
92 lookupStart = ir+1; | |
93 } else { | |
94 x = in; | |
95 lookupStart = x+1; // skip \n for next lookup | |
96 } | |
97 } | |
98 src.position(x); // mark we've consumed up to x | |
99 return dst == null ? src : (ByteBuffer) dst.flip(); | |
100 } | |
101 | |
102 private ByteBuffer nix2win(ByteBuffer src) { | |
103 int x = src.position(); | |
104 ByteBuffer dst = null; | |
105 while (x < src.limit()) { | |
106 int in = indexOf('\n', src, x); | |
107 int ir = indexOf('\r', src, x, in == -1 ? src.limit() : in); | |
108 if (in == -1) { | |
109 if (ir == -1 || allowInconsistent) { | |
110 break; | |
111 } else { | |
112 fail(src, ir); | |
113 } | |
114 } else if (ir != -1 && !allowInconsistent) { | |
115 fail(src, ir); | |
116 } | |
117 | |
118 // x <= in < src.limit | |
119 // allowInconsistent && x <= ir < in || ir == -1 | |
120 if (dst == null) { | |
121 // buffer full of \n grows as much as twice in size | |
122 dst = ByteBuffer.allocate(src.remaining() * 2); | |
123 } | |
124 copySlice(src, x, in, dst); | |
125 if (ir == -1 || ir+1 != in) { | |
126 dst.put((byte) '\r'); | |
127 } // otherwise (ir!=-1 && ir+1==in) we found \r\n pair, don't convert to \r\r\n | |
128 // we may copy \n at src[in] on the next iteration, but would need extra lookupIndex variable then. | |
129 dst.put((byte) '\n'); | |
130 x = in+1; | |
131 } | |
132 src.position(x); | |
133 return dst == null ? src : (ByteBuffer) dst.flip(); | |
134 } | |
135 | |
136 | |
137 private void fail(ByteBuffer b, int pos) { | |
138 throw new RuntimeException(String.format("Inconsistent newline characters in the stream (char 0x%x, local index:%d)", b.get(pos), pos)); | |
139 } | |
140 | |
141 private static int indexOf(char ch, ByteBuffer b, int from) { | |
142 return indexOf(ch, b, from, b.limit()); | |
143 } | |
144 | |
145 // looks up in buf[from..to) | |
146 private static int indexOf(char ch, ByteBuffer b, int from, int to) { | |
147 for (int i = from; i < to; i++) { | |
148 byte c = b.get(i); | |
149 if (ch == c) { | |
150 return i; | |
151 } | |
152 } | |
153 return -1; | |
154 } | |
155 | |
156 public static class Factory implements Filter.Factory { | |
157 private final boolean localIsWin = File.separatorChar == '\\'; // FIXME | |
158 private final boolean failIfInconsistent = true; | |
159 | |
160 public Filter create(HgRepository hgRepo, Path path, Options opts) { | |
161 if (opts.getDirection() == FromRepo) { | |
162 } else if (opts.getDirection() == ToRepo) { | |
163 } | |
164 return new NewlineFilter(failIfInconsistent, 1); | |
165 } | |
166 } | |
167 | |
168 public static void main(String[] args) throws Exception { | |
169 FileInputStream fis = new FileInputStream(new File("/temp/design.lf.txt")); | |
170 FileOutputStream fos = new FileOutputStream(new File("/temp/design.newline.out")); | |
171 ByteBuffer b = ByteBuffer.allocate(12); | |
172 NewlineFilter nlFilter = new NewlineFilter(true, 1); | |
173 while (fis.getChannel().read(b) != -1) { | |
174 b.flip(); // get ready to be read | |
175 ByteBuffer f = nlFilter.filter(b); | |
176 fos.getChannel().write(f); // XXX in fact, f may not be fully consumed | |
177 if (b.hasRemaining()) { | |
178 b.compact(); | |
179 } else { | |
180 b.clear(); | |
181 } | |
182 } | |
183 fis.close(); | |
184 fos.flush(); | |
185 fos.close(); | |
186 } | |
187 | |
188 } |