001: /*
002: * ====================================================================
003: * Copyright (c) 2004-2008 TMate Software Ltd. All rights reserved.
004: *
005: * This software is licensed as described in the file COPYING, which
006: * you should have received as part of this distribution. The terms
007: * are also available at http://svnkit.com/license.html
008: * If newer versions of this license are posted there, you may use a
009: * newer version instead, at your option.
010: * ====================================================================
011: */
012: package org.tmatesoft.svn.core.internal.wc.admin;
013:
014: import java.io.ByteArrayOutputStream;
015: import java.io.File;
016: import java.io.IOException;
017: import java.io.InputStream;
018: import java.io.OutputStream;
019: import java.util.Collections;
020: import java.util.Date;
021: import java.util.HashMap;
022: import java.util.Map;
023: import java.util.StringTokenizer;
024:
025: import org.tmatesoft.svn.core.SVNErrorCode;
026: import org.tmatesoft.svn.core.SVNErrorMessage;
027: import org.tmatesoft.svn.core.SVNException;
028: import org.tmatesoft.svn.core.SVNProperty;
029: import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
030: import org.tmatesoft.svn.core.internal.util.SVNFormatUtil;
031: import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
032: import org.tmatesoft.svn.core.internal.util.SVNTimeUtil;
033: import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
034: import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
035: import org.tmatesoft.svn.core.wc.ISVNOptions;
036:
037: /**
038: * @version 1.1.1
039: * @author TMate Software Ltd.
040: */
041: public class SVNTranslator {
042: public static final byte[] CRLF = new byte[] { '\r', '\n' };
043:
044: public static final byte[] LF = new byte[] { '\n' };
045:
046: public static final byte[] CR = new byte[] { '\r' };
047:
048: public static final byte[] NATIVE = System.getProperty(
049: "line.separator").getBytes();
050:
051: public static void translate(SVNAdminArea adminArea, String name,
052: String srcPath, String dstPath, boolean expand)
053: throws SVNException {
054: translate(adminArea, name, adminArea.getFile(srcPath),
055: adminArea.getFile(dstPath), expand);
056: }
057:
058: public static void translate(SVNAdminArea adminArea, String name,
059: File src, File dst, boolean expand) throws SVNException {
060: File dst2 = dst;
061:
062: SVNVersionedProperties props = adminArea.getProperties(name);
063: String keywords = props.getPropertyValue(SVNProperty.KEYWORDS);
064: String eolStyle = props.getPropertyValue(SVNProperty.EOL_STYLE);
065: boolean special = props.getPropertyValue(SVNProperty.SPECIAL) != null;
066: Map keywordsMap = null;
067: byte[] eols;
068: if (keywords != null) {
069: if (expand) {
070: SVNEntry entry = adminArea.getEntry(name, true);
071: ISVNOptions options = adminArea.getWCAccess()
072: .getOptions();
073: String url = entry.getURL();
074: String author = entry.getAuthor();
075: String date = entry.getCommittedDate();
076: String rev = Long
077: .toString(entry.getCommittedRevision());
078: keywordsMap = computeKeywords(keywords, url, author,
079: date, rev, options);
080: } else {
081: keywordsMap = computeKeywords(keywords, null, null,
082: null, null, null);
083: }
084: }
085: if (!expand) {
086: eols = getBaseEOL(eolStyle);
087: } else {
088: eols = getWorkingEOL(eolStyle);
089: }
090: translate(src, dst2, eols, keywordsMap, special, expand);
091: }
092:
093: public static void translate(File src, File dst, byte[] eol,
094: Map keywords, boolean special, boolean expand)
095: throws SVNException {
096: if (src == null || dst == null) {
097: SVNErrorManager.error(SVNErrorMessage
098: .create(SVNErrorCode.INCORRECT_PARAMS));
099: return;
100: }
101: if (src.equals(dst)) {
102: return;
103: }
104: if (special) {
105: if (dst.exists()) {
106: dst.delete();
107: }
108: if (SVNFileUtil.isWindows) {
109: SVNFileUtil.copyFile(src, dst, true);
110: } else if (expand) {
111: // create symlink to target, and create it at dst
112: SVNFileUtil.createSymlink(dst, src);
113: } else {
114: SVNFileUtil.detranslateSymlink(src, dst);
115: }
116: return;
117:
118: }
119: if (eol == null && (keywords == null || keywords.isEmpty())) {
120: // no expansion, fast copy.
121: SVNFileUtil.copyFile(src, dst, false);
122: return;
123: }
124: OutputStream os = SVNFileUtil.openFileForWriting(dst);
125: OutputStream tos = new SVNTranslatorOutputStream(os, eol,
126: false, keywords, expand);
127: InputStream is = SVNFileUtil.openFileForReading(src);
128: try {
129: copy(is, tos);
130: } catch (IOException e) {
131: SVNErrorMessage err = SVNErrorMessage.create(
132: SVNErrorCode.IO_ERROR, e.getLocalizedMessage());
133: SVNErrorManager.error(err, e);
134: } finally {
135: SVNFileUtil.closeFile(tos);
136: SVNFileUtil.closeFile(os);
137: SVNFileUtil.closeFile(is);
138: }
139: }
140:
141: public static boolean checkNewLines(File file) {
142: if (file == null || !file.exists() || file.isDirectory()) {
143: return true;
144: }
145: InputStream is = null;
146: try {
147: is = SVNFileUtil.openFileForReading(file);
148: int r;
149: byte[] lastFoundEOL = null;
150: byte[] currentEOL = null;
151: while ((r = is.read()) >= 0) {
152: if (r == '\n') {
153: currentEOL = LF;
154: } else if (r == '\r') {
155: currentEOL = CR;
156: r = is.read();
157: if (r == '\n') {
158: currentEOL = CRLF;
159: }
160: }
161: if (lastFoundEOL == null) {
162: lastFoundEOL = currentEOL;
163: } else if (currentEOL != null
164: && lastFoundEOL != currentEOL) {
165: return false;
166: }
167: }
168: } catch (IOException e) {
169: return false;
170: } catch (SVNException e) {
171: return false;
172: } finally {
173: SVNFileUtil.closeFile(is);
174: }
175: return true;
176: }
177:
178: public static void copy(InputStream src, OutputStream dst)
179: throws IOException {
180: byte[] buffer = new byte[8192];
181: while (true) {
182: int read = src.read(buffer);
183: if (read <= 0) {
184: return;
185: }
186: dst.write(buffer, 0, read);
187: }
188: }
189:
190: public static Map computeKeywords(String keywords, String u,
191: String a, String d, String r, ISVNOptions options) {
192: if (keywords == null) {
193: return Collections.EMPTY_MAP;
194: }
195: boolean expand = u != null;
196: byte[] date = null;
197: byte[] idDate = null;
198: byte[] url = null;
199: byte[] rev = null;
200: byte[] author = null;
201: byte[] name = null;
202: byte[] id = null;
203:
204: Date jDate = d == null ? null : SVNTimeUtil.parseDate(d);
205:
206: Map map = new HashMap();
207: try {
208: for (StringTokenizer tokens = new StringTokenizer(keywords,
209: " \t\n\b\r\f"); tokens.hasMoreTokens();) {
210: String token = tokens.nextToken();
211: if ("LastChangedDate".equalsIgnoreCase(token)
212: || "Date".equalsIgnoreCase(token)) {
213: date = expand && date == null ? SVNFormatUtil
214: .formatHumanDate(jDate, options).getBytes(
215: "UTF-8") : date;
216: map.put("LastChangedDate", date);
217: map.put("Date", date);
218: } else if ("LastChangedRevision"
219: .equalsIgnoreCase(token)
220: || "Revision".equalsIgnoreCase(token)
221: || "Rev".equalsIgnoreCase(token)) {
222: rev = expand && rev == null ? r.getBytes("UTF-8")
223: : rev;
224: map.put("LastChangedRevision", rev);
225: map.put("Revision", rev);
226: map.put("Rev", rev);
227: } else if ("LastChangedBy".equalsIgnoreCase(token)
228: || "Author".equalsIgnoreCase(token)) {
229: author = expand && author == null ? (a == null ? new byte[0]
230: : a.getBytes("UTF-8"))
231: : author;
232: map.put("LastChangedBy", author);
233: map.put("Author", author);
234: } else if ("HeadURL".equalsIgnoreCase(token)
235: || "URL".equalsIgnoreCase(token)) {
236: url = expand && url == null ? SVNEncodingUtil
237: .uriDecode(u).getBytes("UTF-8") : url;
238: map.put("HeadURL", url);
239: map.put("URL", url);
240: } else if ("Id".equalsIgnoreCase(token)) {
241: if (expand && id == null) {
242: rev = rev == null ? r.getBytes("UTF-8") : rev;
243: idDate = idDate == null ? SVNFormatUtil
244: .formatDate(jDate).getBytes("UTF-8")
245: : idDate;
246: name = name == null ? SVNEncodingUtil
247: .uriDecode(SVNPathUtil.tail(u))
248: .getBytes("UTF-8") : name;
249: author = author == null ? (a == null ? new byte[0]
250: : a.getBytes("UTF-8"))
251: : author;
252: ByteArrayOutputStream bos = new ByteArrayOutputStream();
253: bos.write(name);
254: bos.write(' ');
255: bos.write(rev);
256: bos.write(' ');
257: bos.write(idDate);
258: bos.write(' ');
259: bos.write(author);
260: bos.close();
261: id = bos.toByteArray();
262: }
263: map.put("Id", expand ? id : null);
264: }
265: }
266: } catch (IOException e) {
267: //
268: }
269: return map;
270: }
271:
272: public static byte[] getEOL(String propertyValue) {
273: if ("native".equals(propertyValue)) {
274: return NATIVE;
275: } else if ("LF".equals(propertyValue)) {
276: return LF;
277: } else if ("CR".equals(propertyValue)) {
278: return CR;
279: } else if ("CRLF".equals(propertyValue)) {
280: return CRLF;
281: }
282: return null;
283: }
284:
285: public static byte[] getBaseEOL(String eolStyle) {
286: if ("native".equals(eolStyle)) {
287: return LF;
288: } else if ("CR".equals(eolStyle)) {
289: return CR;
290: } else if ("LF".equals(eolStyle)) {
291: return LF;
292: } else if ("CRLF".equals(eolStyle)) {
293: return CRLF;
294: }
295: return null;
296: }
297:
298: public static byte[] getWorkingEOL(String eolStyle) {
299: if ("native".equals(eolStyle)) {
300: return NATIVE;
301: } else if ("CR".equals(eolStyle)) {
302: return CR;
303: } else if ("LF".equals(eolStyle)) {
304: return LF;
305: } else if ("CRLF".equals(eolStyle)) {
306: return CRLF;
307: }
308: return null;
309: }
310: }
|